General Setup

Setup chunk

Setup reticulate

knitr::opts_chunk$set(fig.width = 8)
knitr::opts_knit$set(root.dir = normalizePath(".."))
knitr::opts_knit$get("root.dir")
[1] "/nas/groups/treutlein/USERS/tomasgomes/projects/liver_regen"

Load libraries

library(reticulate)
knitr::knit_engines$set(python = reticulate::eng_python)
py_available(initialize = FALSE)
[1] TRUE
use_python(Sys.which("python"))
py_config()
python:         /home/tpires/bin/miniconda3/bin/python
libpython:      /home/tpires/bin/miniconda3/lib/libpython3.8.so
pythonhome:     /home/tpires/bin/miniconda3:/home/tpires/bin/miniconda3
version:        3.8.3 (default, May 19 2020, 18:47:26)  [GCC 7.3.0]
numpy:          /home/tpires/bin/miniconda3/lib/python3.8/site-packages/numpy
numpy_version:  1.18.5

NOTE: Python version was forced by RETICULATE_PYTHON

Load and preprocess data

Load data (from all cells)

library(Seurat)
library(ggplot2)
library(ggridges)
library(dplyr)
library(igraph)
library(data.table)

Prepare a global object with all necessary metadata

allcells_css = readRDS(file = "data/processed/allcells_css.RDS")
end_cells = readRDS(file = "results/endothelial/only_end_cells_zon.RDS")
hep_cells = readRDS(file = "results/zonation_cond/hep_cells_zonation_rank.RDS")
imm_cells = readRDS(file = "results/immune/all_imm_cells.RDS")

Subset and process each condition

endpop_df = data.frame(row.names = colnames(end_cells),
                       "subpops" = end_cells@meta.data$endo_simp)
immpop_df = data.frame(row.names = colnames(imm_cells),
                       "subpops" = imm_cells@meta.data$immune_annot)
heppop_df = lapply(hep_cells, function(x) cbind(colnames(x), as.character(x$zonation_int)))
heppop_df = Reduce(rbind, heppop_df)
heppop_df = data.frame(row.names = heppop_df[,1],
                       subpops = factor(heppop_df[,2]))
levels(heppop_df$subpops) = c("(-0.00099,0.333]" = "Hepatocytes_Z1",
                              "(0.333,0.667]" = "Hepatocytes_Z2",
                              "(0.667,1]" = "Hepatocytes_Z3")
heppop_df$subpops = as.character(heppop_df$subpops)

subpop_df = rbind(endpop_df, immpop_df, heppop_df)
subpop_df$subpops[subpop_df$subpops=="Cycling cells"] = "Dividing endothelial cells"

# add subpop metadata
allcells_css = AddMetaData(allcells_css, subpop_df)
allcells_css$subpops[is.na(allcells_css$subpops)] = allcells_css$allcells_major[is.na(allcells_css$subpops)]
allcells_css$subpops[allcells_css$subpops=="Hepatocyte-Monocyte interaction"] = "Doublets"

# the cells in this object named "Hepatocytes", "Endothelial cells", and "Doublets" have to be removed
## the first two are cells that didn't pass the hep and end analysis
sub_allcells_css = allcells_css[,!(allcells_css$subpops %in% c("Hepatocytes", "Endothelial cells",
                                                               "Doublets"))]

# add simplified column - merge some populations, don't include cycling pops and stressed T cells
sub_allcells_css$subpops_simp = sub_allcells_css$subpops
sub_allcells_css$subpops_simp[grepl("MAIT", sub_allcells_css$subpops_simp)] = "MAIT cells"
sub_allcells_css$subpops_simp[grepl("NK cells ", sub_allcells_css$subpops_simp)] = "NK cells"
sub_allcells_css$subpops_simp[grepl("LSEC (high MT", sub_allcells_css$subpops_simp, 
                                    fixed = T)] = "LSEC (high MT)"
sub_allcells_css$subpops_simp[grepl("CD8 ab-T ", sub_allcells_css$subpops_simp, 
                                    fixed = T)] = "CD8 ab-T cells"
simp_allcells_css = sub_allcells_css[,!(sub_allcells_css$subpops_simp %in% c("ab-T cells (stress)", "Dividing cDCs", "Dividing endothelial cells","Dividing T/NK cells"))]

Running CellPhoneDB

Commands for runnin CellPhoneDB will be written here. Results are output to the local1 disk to avoid I/O issues, but the output folders should then be copied to: results/cell_comm_healthy/CellPhoneDB_runs.
NOTE: for some reason I’m getting a SegFault when trying to run the heatmap_plot command. This was run on the Euler server instead.

#cpdb_path = "results/cell_comm/CellPhoneDB_runs_updt"
cpdb_path = "/local1/USERS/tomasgomes/CellPhoneDB_runs_updt"

cond_cells = list()
for(cond in unique(sub_allcells_css@meta.data$Condition)){
  # subset condition
  cond_cells[[cond]] = sub_allcells_css[,sub_allcells_css@meta.data$Condition==cond]
  
  # define metadata
  meta = data.frame(row.names = rownames(cond_cells[[cond]]@meta.data),
                    "Cell" = rownames(cond_cells[[cond]]@meta.data), 
                    "cell_type" = as.character(cond_cells[[cond]]@meta.data$subpops),
                    stringsAsFactors = F)
  write.table(meta, file = paste0(cpdb_path, "/", cond, "/", cond, "_meta_names.txt"), 
            sep = "\t", col.names = T, row.names = F, quote = F)
  
  # normalise and save
  cond_cells[[cond]] = suppressWarnings(SCTransform(cond_cells[[cond]], do.correct.umi = T, 
                                                    vars.to.regress=c("unique_name", "nCount_RNA"),
                                                    variable.features.rv.th = 1, seed.use = 1,
                                                    return.only.var.genes = F, verbose = F,
                                                    variable.features.n = NULL))
  
  dat = cbind(rownames(cond_cells[[cond]]@assays$SCT@data),
              Matrix::as.matrix(cond_cells[[cond]]@assays$SCT@data))
  colnames(dat)[1] = "Gene"
  write.table(dat, file = paste0(cpdb_path, "/", cond, "/", cond, "_exp_norm.txt"), 
              sep = "\t", col.names = T, row.names = F, quote = F)
  
  
  # subset condition
  cond_cells[[cond]] = simp_allcells_css[,simp_allcells_css@meta.data$Condition==cond]
  
  # define metadata
  meta_simp = data.frame(row.names = rownames(cond_cells[[cond]]@meta.data),
                         "Cell" = rownames(cond_cells[[cond]]@meta.data), 
                         "cell_type" = as.character(cond_cells[[cond]]@meta.data$subpops_simp),
                         stringsAsFactors = F)
  write.table(meta_simp, file = paste0(cpdb_path, "/", cond, "_simp/", cond, "_meta_names.txt"), 
            sep = "\t", col.names = T, row.names = F, quote = F)
   
  # normalise and save
  cond_cells[[cond]] = suppressWarnings(SCTransform(cond_cells[[cond]], do.correct.umi = T, 
                                                    vars.to.regress=c("unique_name", "nCount_RNA"),
                                                    variable.features.rv.th = 1, seed.use = 1,
                                                    return.only.var.genes = F, verbose = F,
                                                    variable.features.n = NULL))
  
  dat = cbind(rownames(cond_cells[[cond]]@assays$SCT@data),
              Matrix::as.matrix(cond_cells[[cond]]@assays$SCT@data))
  colnames(dat)[1] = "Gene"
  write.table(dat, file = paste0(cpdb_path, "/", cond, "_simp/", cond, "_exp_norm.txt"), 
              sep = "\t", col.names = T, row.names = F, quote = F)
}
for(n in names(cond_cells)){
  comm1 = paste0("cellphonedb method statistical_analysis /local1/USERS/tomasgomes/CellPhoneDB_runs_updt/", n, "_simp/", n, "_meta_names.txt /local1/USERS/tomasgomes/CellPhoneDB_runs_updt/", n, "_simp/", n, "_exp_norm.txt --threads=12 --output-path=/local1/USERS/tomasgomes/liver/CellPhoneDB_runs_updt/", n, "_simp --project-name ", n, "_simp --counts-data gene_name")
  comm2 = paste0("cp -r /local1/USERS/tomasgomes/liver/CellPhoneDB_runs_updt/", n, "_simp/", n, "_simp results/cell_comm/CellPhoneDB_runs_updt/")
  comm3 = paste0("cp -r /local1/USERS/tomasgomes/CellPhoneDB_runs_updt/", n, "_simp/", n, "_meta_names.txt results/cell_comm/CellPhoneDB_runs_updt/", n, "_simp/")
  comm4 = paste0("cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_runs_updt/", n, "_simp/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_runs_updt/", n, "_simp/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt /local1/USERS/tomasgomes/CellPhoneDB_runs_updt/", n, "_simp/", n, "_meta_names.txt")
  
  print(n)
  print(comm1)
  print(comm2)
  print(comm3)
  print(comm4)
  print(".")
}

Process results

Load results

cpdb_path = "results/cell_comm/CellPhoneDB_runs_updt"

sig_means_names_l = list()
dec_l = list()
net_names_l = list()
meta_l = list()
conds = unique(allcells_css@meta.data$Condition)
for(n in c(conds, paste0(conds, "_simp"))){
  ns = strsplit(n, "_")[[1]][1]
  sig_means_names_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/significant_means.txt"),
                                      header = T, sep = "\t", stringsAsFactors = F)
  dec_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/deconvoluted.txt"),
                          header = T, sep = "\t")
  colnames(dec_l[[n]]) = gsub(".", " ", colnames(dec_l[[n]]), fixed = T)
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="gd T cells"] = "gd-T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Macrophages  HES4  "] = "Macrophages (HES4+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells"] = "CD8 ab-T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 1"] = "CD8 ab-T cells 1"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 2"] = "CD8 ab-T cells 2"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 3"] = "CD8 ab-T cells 3"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Naive CD4  T cells"] = "Naive CD4+ T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="ab T cells  stress "] = "ab-T cells (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  secretory "] = "Monocytes (secretory)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  TREM2  CD9  "] = "Monocytes (TREM2+ CD9+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  IGSF21  GPR34  "] = "Monocytes (IGSF21+ GPR34+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  stress "] = "LSEC (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  remodelling "] = "LSEC (remodelling)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  interferon "] = "LSEC (interferon)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT 2 "] = "LSEC (high MT 2)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT 1 "] = "LSEC (high MT 1)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT "] = "LSEC (high MT)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  fenestr  "] = "LSEC (fenestr.)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Kupffer cells  SUCNR1  "] = "Kupffer cells (SUCNR1+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="IgG  Plasma cells"] = "IgG+ Plasma cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="IgA  Plasma cells"] = "IgA+ Plasma cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="EC non LSEC"] = "EC non-LSEC"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Dividing T NK cells"] = "Dividing T/NK cells"
  net_names_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/count_net.txt"),
                                header = T, sep = "\t", stringsAsFactors = F)
  meta_l[[n]] = if(!grepl("simp", n)){
    read.table(paste0(cpdb_path, "/", n, "/", n, "_meta_names.txt"),header = T,sep = "\t")
  } else{
    read.table(paste0(cpdb_path, "/", n, "/", ns, "_meta_names.txt"),header = T,sep = "\t")
  }
}
saveRDS(dec_l, file = "results/cell_comm/updt/deconvoluted_list.RDS")
saveRDS(net_names_l, file = "results/cell_comm/updt/count_net_list.RDS")

Reformat significant means matrix

cpdb_path = "results/cell_comm/CellPhoneDB_runs_updt"

sig_means_names_l = list()
dec_l = list()
net_names_l = list()
meta_l = list()
conds = unique(allcells_css@meta.data$Condition)
for(n in c(conds, paste0(conds, "_simp"))){
  ns = strsplit(n, "_")[[1]][1]
  sig_means_names_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/significant_means.txt"),
                                      header = T, sep = "\t", stringsAsFactors = F)
  dec_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/deconvoluted.txt"),
                          header = T, sep = "\t")
  colnames(dec_l[[n]]) = gsub(".", " ", colnames(dec_l[[n]]), fixed = T)
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="gd T cells"] = "gd-T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Macrophages  HES4  "] = "Macrophages (HES4+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells"] = "CD8 ab-T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 1"] = "CD8 ab-T cells 1"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 2"] = "CD8 ab-T cells 2"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 3"] = "CD8 ab-T cells 3"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Naive CD4  T cells"] = "Naive CD4+ T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="ab T cells  stress "] = "ab-T cells (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  secretory "] = "Monocytes (secretory)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  TREM2  CD9  "] = "Monocytes (TREM2+ CD9+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  IGSF21  GPR34  "] = "Monocytes (IGSF21+ GPR34+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  stress "] = "LSEC (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  remodelling "] = "LSEC (remodelling)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  interferon "] = "LSEC (interferon)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT 2 "] = "LSEC (high MT 2)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT 1 "] = "LSEC (high MT 1)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT "] = "LSEC (high MT)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  fenestr  "] = "LSEC (fenestr.)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Kupffer cells  SUCNR1  "] = "Kupffer cells (SUCNR1+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="IgG  Plasma cells"] = "IgG+ Plasma cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="IgA  Plasma cells"] = "IgA+ Plasma cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="EC non LSEC"] = "EC non-LSEC"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Dividing T NK cells"] = "Dividing T/NK cells"
  net_names_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/count_net.txt"),
                                header = T, sep = "\t", stringsAsFactors = F)
  meta_l[[n]] = if(!grepl("simp", n)){
    read.table(paste0(cpdb_path, "/", n, "/", n, "_meta_names.txt"),header = T,sep = "\t")
  } else{
    read.table(paste0(cpdb_path, "/", n, "/", ns, "_meta_names.txt"),header = T,sep = "\t")
  }
}
saveRDS(dec_l, file = "results/cell_comm/updt/deconvoluted_list.RDS")
saveRDS(net_names_l, file = "results/cell_comm/updt/count_net_list.RDS")

Plot interaction counts

inter_counts_l = list()
for(n in names(net_names_l)){
  inter_counts = reshape2::dcast(data = net_names_l[[n]], 
                                 formula = SOURCE~TARGET, value.var = "count")
  rownames(inter_counts) = inter_counts[,1]
  inter_counts = inter_counts[,-1]
  
  pdf(paste0("results/cell_comm/updt/", n, "_number_of_interactions.pdf"), useDingbats = F, 
      height = 10, width = 10)
  pheatmap::pheatmap(inter_counts, clustering_method = "ward.D2", main = "All clusters")
  dev.off()
  inter_counts_l[[n]] = inter_counts
}

Making two cell type by interaction matrices: one has the ligand mean, another the receptor mean

inter_counts_l = list()
for(n in names(net_names_l)){
  inter_counts = reshape2::dcast(data = net_names_l[[n]], 
                                 formula = SOURCE~TARGET, value.var = "count")
  rownames(inter_counts) = inter_counts[,1]
  inter_counts = inter_counts[,-1]
  
  pdf(paste0("results/cell_comm/updt/", n, "_number_of_interactions.pdf"), useDingbats = F, 
      height = 10, width = 10)
  pheatmap::pheatmap(inter_counts, clustering_method = "ward.D2", main = "All clusters")
  dev.off()
  inter_counts_l[[n]] = inter_counts
}

Making two cell type by interaction matrices: one has the ligand mean, another the receptor mean (here only directional)

scoring_mats = list()
for(n in names(reform_list)){
  cl_reform = reform_list[[n]]
  # add reversed non-directed interactions, to consider them in both directions
  cl_reform0 = cl_reform[cl_reform$dir==0,]
  cl_reform0 = cl_reform0[,c(1,3,2,5,4,6,7)]
  colnames(cl_reform0) = colnames(cl_reform)
  cl_reform = rbind(cl_reform, cl_reform0)
  
  dec_cl = dec_l[[n]]
  
  mat_lig_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  mat_rec_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  colnames(mat_lig_cl) = colnames(mat_rec_cl) = unique(cl_reform$ct1)
  rownames(mat_lig_cl) = rownames(mat_rec_cl) = unique(cl_reform$id_cp_interaction)
  for(i in 1:nrow(cl_reform)){
    g1 = cl_reform[i,"lr1"]
    g2 = cl_reform[i,"lr2"]
    
    c1 = cl_reform[i,"ct1"]
    c2 = cl_reform[i,"ct2"]
    
    int = as.character(cl_reform[i,1])
    
    m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g1,c1], na.rm = T)
    if(is.na(m1)) m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g1,c1], 
                            na.rm = T)
    mat_lig_cl[int, c1] = m1
    
    m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g2,c2], na.rm = T)
    if(is.na(m2)) m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g2,c2],
                            na.rm = T)
    if(is.na(m1) | is.na(m2)) print(int)
    mat_rec_cl[int, c2] = m2
  }
  
  # ligand vs receptor correlation
  cor_mat_each_cl = cor(mat_lig_cl, mat_rec_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats[[n]] = list("mat_lig_ct" = mat_lig_cl, "mat_rec_ct" = mat_rec_cl,
                           "cor_mat_each" = cor_mat_each_cl)
  
  # sum(lig, reg) correlation
  mat_lig_cl[is.na(mat_lig_cl)] = 0
  mat_rec_cl[is.na(mat_rec_cl)] = 0
  mat_both_cl = mat_lig_cl+mat_rec_cl
  mat_both_cl[mat_both_cl==0] = NA
  cor_mat_both_cl = cor(mat_both_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats[[n]]$cor_mat_both = cor_mat_both_cl
}
saveRDS(scoring_mats, file = "results/cell_comm/updt/scoring_mats.RDS")

Count interactions of each type

scoring_mats_dir = list()
for(n in names(reform_list)){
  cl_reform = reform_list[[n]]
  # add reversed non-directed interactions, to consider them in both directions
  cl_reform0 = cl_reform[cl_reform$dir==0,]
  cl_reform0 = cl_reform0[,c(1,3,2,5,4,6,7)]
  colnames(cl_reform0) = colnames(cl_reform)
  cl_reform = rbind(cl_reform, cl_reform0)
  
  cl_reform = cl_reform[cl_reform$dir==1,]
  
  dec_cl = dec_l[[n]]
  
  mat_lig_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  mat_rec_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  colnames(mat_lig_cl) = colnames(mat_rec_cl) = unique(cl_reform$ct1)
  rownames(mat_lig_cl) = rownames(mat_rec_cl) = unique(cl_reform$id_cp_interaction)
  for(i in 1:nrow(cl_reform)){
    g1 = cl_reform[i,"lr1"]
    g2 = cl_reform[i,"lr2"]
    
    c1 = cl_reform[i,"ct1"]
    c2 = cl_reform[i,"ct2"]
    
    int = as.character(cl_reform[i,1])
    
    m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g1,c1], na.rm = T)
    if(is.na(m1)) m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g1,c1], 
                            na.rm = T)
    mat_lig_cl[int, c1] = m1
    
    m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g2,c2], na.rm = T)
    if(is.na(m2)) m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g2,c2],
                            na.rm = T)
    if(is.na(m1) | is.na(m2)) print(int)
    mat_rec_cl[int, c2] = m2
  }
  
  # ligand vs receptor correlation
  cor_mat_each_cl = cor(mat_lig_cl, mat_rec_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats_dir[[n]] = list("mat_lig_ct" = mat_lig_cl, "mat_rec_ct" = mat_rec_cl,
                               "cor_mat_each" = cor_mat_each_cl)
  
  # sum(lig, reg) correlation
  mat_lig_cl[is.na(mat_lig_cl)] = 0
  mat_rec_cl[is.na(mat_rec_cl)] = 0
  mat_both_cl = mat_lig_cl+mat_rec_cl
  mat_both_cl[mat_both_cl==0] = NA
  cor_mat_both_cl = cor(mat_both_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats_dir[[n]]$cor_mat_both = cor_mat_both_cl
}
saveRDS(scoring_mats_dir, file = "results/cell_comm/updt/scoring_mats_dir.RDS")

Interaction analysis in conditions

Compare interactions with DE genes

inter_counts_dir = list()
for(n in names(reform_list)){
  inter_counts_dir[[n]] = list()
  for(i in c(0,1)){
    cl_reform = reform_list[[n]]
    cl_reform = cl_reform[cl_reform$dir==i,]
    
    n_dir_int = matrix(0, nrow = length(unique(cl_reform$ct1)),
                       ncol = length(unique(cl_reform$ct1)))
    rownames(n_dir_int) = colnames(n_dir_int) = unique(cl_reform$ct1)
    for(c1 in unique(cl_reform$ct1)){
      for(c2 in unique(cl_reform$ct1)){
        n_dir_int[c1,c2] = nrow(cl_reform[cl_reform$ct1==c1 & cl_reform$ct2==c2,])
        if(i==0){
          n_dir_int[c1,c2] = n_dir_int[c1,c2]+nrow(cl_reform[cl_reform$ct1==c2 &
                                                               cl_reform$ct2==c1,])
        }
      }
    }
    inter_counts_dir[[n]][[as.character(i)]] = n_dir_int
  }
}
saveRDS(inter_counts_dir, file = "results/cell_comm/updt/inter_counts_dir.RDS")

Plot interaction heatmap - total and filtered

# add genes from complexes
complex_ref = unique(rbind(dec_l$healthy[dec_l$healthy$is_complex=="True",c(1,5)], 
                           dec_l$embolised[dec_l$embolised$is_complex=="True",c(1,5)],
                           dec_l$regenerating[dec_l$regenerating$is_complex=="True",c(1,5)],
                           dec_l$healthy_simp[dec_l$healthy_simp$is_complex=="True",c(1,5)],
                           dec_l$embolised_simp[dec_l$embolised_simp$is_complex=="True",c(1,5)],
                           dec_l$regenerating_simp[dec_l$regenerating_simp$is_complex=="True",c(1,5)]))
inter_df = list()
for(n in names(reform_list)){
  tmp = merge(reform_list[[n]], complex_ref, by.x = 4, by.y = 2, all.x = T)[,c(2,3,4,1,5,8,6,7)]
  inter_df[[n]] = merge(tmp, complex_ref, by.x = 5, by.y = 2, all.x = T)[,c(2,3,4,5,6,1,9,7,8)]
  inter_df[[n]]$gene_name.x[is.na(inter_df[[n]]$gene_name.x)] = inter_df[[n]]$lr1[is.na(inter_df[[n]]$gene_name.x)]
  inter_df[[n]]$gene_name.y[is.na(inter_df[[n]]$gene_name.y)] = inter_df[[n]]$lr2[is.na(inter_df[[n]]$gene_name.y)]
  colnames(inter_df[[n]])[c(5,7)] = c("gn1", "gn2")
}

for(cc in names(inter_df)){
  # interaction unique in condition
  unique_inter = setdiff(unique(inter_df[[cc]]$id_cp_interaction),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$id_cp_interaction,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$id_cp_interaction)))
  inter_df[[cc]]$cpdb_unique = inter_df[[cc]]$id_cp_interaction %in% unique_inter
  
  # lr1 unique in condition
  unique_lr1 = setdiff(unique(inter_df[[cc]]$lr1),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$lr1,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$lr1)))
  inter_df[[cc]]$cpdb_unique_lr1 = inter_df[[cc]]$lr1 %in% unique_lr1
  
  # lr2 unique in condition
  unique_lr2 = setdiff(unique(inter_df[[cc]]$lr2),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$lr2,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$lr2)))
  inter_df[[cc]]$cpdb_unique_lr2 = inter_df[[cc]]$lr2 %in% unique_lr2
}
for(cc in names(inter_df)){
  oc1 = names(inter_df)[names(inter_df)!=cc][1]
  oc2 = names(inter_df)[names(inter_df)!=cc][2]
  int1 = paste0(inter_df[[cc]]$id_cp_interaction,inter_df[[cc]]$ct1,inter_df[[cc]]$ct2)
  int2 = unique(paste0(inter_df[[oc1]]$id_cp_interaction,inter_df[[oc1]]$ct1,inter_df[[oc1]]$ct2))
  int3 = unique(paste0(inter_df[[oc2]]$id_cp_interaction,inter_df[[oc2]]$ct1,inter_df[[oc2]]$ct2))
  unique_inter = setdiff(unique(int1), unique(c(int2, int3)))
  inter_df[[cc]]$cpdb_unique_ct = int1 %in% unique_inter
}
for(n in names(inter_df)){
  df = inter_df[[n]]
  df = unique(df[,c(1:3)])
  
  ints_un_ct = rowSums(table(df$id_cp_interaction, df$ct1)>0)<4 | 
    rowSums(table(df$id_cp_interaction, df$ct2)>0)<4
  
  inter_df[[n]]$inter_ct_spec = inter_df[[n]]$id_cp_interaction %in% names(ints_un_ct)[ints_un_ct]
}
for(n in names(inter_df)){
  xxx = data.frame(ct = c(inter_df[[n]][,2], inter_df[[n]][,3]), 
                   lr = c(inter_df[[n]][,4], inter_df[[n]][,6]))
  xxx = unique(xxx)
  tab_lr = (table(xxx$lr, xxx$ct)>0)*1
  tab_lr = rowSums(tab_lr)
  
  inter_df[[n]]$lr1_nct = tab_lr[inter_df[[n]]$lr1]
  inter_df[[n]]$lr2_nct = tab_lr[inter_df[[n]]$lr2]
}
saveRDS(inter_df, file = "results/cell_comm/updt/cond_diff_interact_DE.RDS")

Get non-ubiquitous interactions


int_counts_total = list()
int_counts_filt = list()
for(n in names(inter_df)){
  subdfct = unique(inter_df[[n]][,1:3])
  subdfct = unique(subdfct)
  dfct = data.frame("ct1" = c(subdfct$ct1, subdfct$ct2),
                    "ct2" = c(subdfct$ct2, subdfct$ct1))
  int_counts_total[[n]] = dfct
  pheatmap::pheatmap(table(dfct$ct1, dfct$ct2), main = n)
  
  keep = inter_df[[n]]$lr1_nct<=1 | inter_df[[n]]$lr2_nct<=1
  subdfct = unique(inter_df[[n]][keep,c(1:3, 15,16)])
  swp = subdfct[,5]==1
  tmp = subdfct$ct2[swp]
  subdfct$ct2[swp] = subdfct$ct1[swp]
  subdfct$ct1[swp] = tmp
  tmp = subdfct$lr2_nct[swp]
  subdfct$lr2_nct[swp] = subdfct$lr1_nct[swp]
  subdfct$lr1_nct[swp] = tmp
  dup = subdfct[subdfct$lr1_nct==1 & subdfct$lr2_nct==1,]
  tmp = dup$ct2
  dup$ct2 = dup$ct1
  dup$ct1 = tmp
  subdfct = rbind(subdfct, dup)
  subdfct = unique(subdfct[,1:3])
  int_counts_filt[[n]] = dfct
  pheatmap::pheatmap(table(subdfct$ct1, subdfct$ct2), main = n)
}
saveRDS(int_counts_total, file = "results/cell_comm/updt/int_counts_total.RDS")

saveRDS(int_counts_filt, file = "results/cell_comm/updt/int_counts_filt.RDS")

Annotate interactions

inter_nonss = setdiff(unique(c(inter_df$embolised$id_cp_interaction,
                               inter_df$regenerating$id_cp_interaction)),
                      unique(inter_df$healthy$id_cp_interaction))
inter_nonemb = setdiff(unique(c(inter_df$healthy$id_cp_interaction,
                               inter_df$regenerating$id_cp_interaction)),
                      unique(inter_df$embolised$id_cp_interaction))
inter_nonreg = setdiff(unique(c(inter_df$embolised$id_cp_interaction,
                               inter_df$healthy$id_cp_interaction)),
                      unique(inter_df$regenerating$id_cp_interaction))

ct_g_cond = list()
for(n in names(inter_df)[1:3]){
  df = inter_df[[n]][inter_df[[n]]$cpdb_unique | 
                       inter_df[[n]]$id_cp_interaction %in% c(inter_nonss, inter_nonemb, inter_nonreg),]
  #df = df[df$lr1_nct<=1 | df$lr2_nct<=1,]
  
  cts = unique(c(df$ct1, df$ct2))
  ct_g_list = list()
  for(ct in cts){
    ct_g_list[[ct]] = data.frame("interact" = c(as.character(df$id_cp_interaction[df$ct1==ct]),
                                                as.character(df$id_cp_interaction[df$ct2==ct])),
                                 "gene" = c(as.character(df$gn1[df$ct1==ct]),
                                            as.character(df$gn2[df$ct2==ct])),
                                 "gene_target" = c(as.character(df$gn2[df$ct1==ct]),
                                                   as.character(df$gn1[df$ct2==ct])),
                                 "ct" = ct,
                                 "ct_target" = c(as.character(df$ct2[df$ct1==ct]),
                                                as.character(df$ct1[df$ct2==ct])),
                                 "cond" = n, stringsAsFactors = F)
  }
  ct_g_cond[[n]] = unique(Reduce(rbind, ct_g_list))
}
ct_g_cond = Reduce(rbind, ct_g_cond)
dim(ct_g_cond)
[1] 13141     6
length(unique(ct_g_cond$interact))
[1] 177
inter_nonss = setdiff(unique(c(inter_df$embolised_simp$id_cp_interaction,
                               inter_df$regenerating_simp$id_cp_interaction)),
                      unique(inter_df$healthy_simp$id_cp_interaction))
inter_nonemb = setdiff(unique(c(inter_df$healthy_simp$id_cp_interaction,
                               inter_df$regenerating_simp$id_cp_interaction)),
                      unique(inter_df$embolised_simp$id_cp_interaction))
inter_nonreg = setdiff(unique(c(inter_df$embolised_simp$id_cp_interaction,
                               inter_df$healthy_simp$id_cp_interaction)),
                      unique(inter_df$regenerating_simp$id_cp_interaction))

cts_g_cond = list()
for(n in names(inter_df)[4:6]){
  df = inter_df[[n]][inter_df[[n]]$cpdb_unique | 
                       inter_df[[n]]$id_cp_interaction %in% c(inter_nonss, inter_nonemb, inter_nonreg),]
  #df = df[df$lr1_nct<=1 | df$lr2_nct<=1,]
  
  cts = unique(c(df$ct1, df$ct2))
  ct_g_list = list()
  for(ct in cts){
    ct_g_list[[ct]] = data.frame("interact" = c(as.character(df$id_cp_interaction[df$ct1==ct]),
                                                as.character(df$id_cp_interaction[df$ct2==ct])),
                                 "gene" = c(as.character(df$gn1[df$ct1==ct]),
                                            as.character(df$gn2[df$ct2==ct])),
                                 "gene_target" = c(as.character(df$gn2[df$ct1==ct]),
                                                   as.character(df$gn1[df$ct2==ct])),
                                 "ct" = ct,
                                 "ct_target" = c(as.character(df$ct2[df$ct1==ct]),
                                                as.character(df$ct1[df$ct2==ct])),
                                 "cond" = n, stringsAsFactors = F)
  }
  cts_g_cond[[n]] = unique(Reduce(rbind, ct_g_list))
}
cts_g_cond = Reduce(rbind, cts_g_cond)
dim(cts_g_cond)
[1] 10449     6
length(unique(cts_g_cond$interact))
[1] 178
ct_g_l = list("cts_g_cond" = cts_g_cond,
              "ct_g_cond" = ct_g_cond)

Get expression for each interaction in each condition

inter_annot = read.csv("results/cell_comm/updt/inter_unique5.csv", header = T, stringsAsFactors = F)
#xxx = merge(unique(rbind(ct_g_cond[,1:3], cts_g_cond[,1:3])), 
#            unique(inter_annot[,c(1,4)]), by = 1, all.x = T)
#write.csv(xxx, file = "inter_unique5.csv", row.names = F, col.names = T, quote = F)

inter_annot = unique(inter_annot[,c(1,4)])

description = strsplit(inter_annot$description, ";")
inter_des = lapply(1:length(description), 
                   function(x) rep(inter_annot$interact[x], length(description[[x]])))
inter_annot = data.frame("inter" = unlist(inter_des),
                         "funct" = unlist(description))

inter_annot$funct[inter_annot$funct=="intercellular adhesion"] = "adhesion"
inter_annot$funct[inter_annot$funct=="antibody regulation"] = "immune regulation"
inter_annot$funct[inter_annot$funct=="antigen presentation"] = "immune activity"

write.csv(inter_annot, file = "data/interaction_annotation2.csv", 
          row.names = F, col.names = T, quote = F)
attempt to set 'col.names' ignored
colnames(inter_annot) = c("interact", "description")

Variability of interactions

ct_int_exp_l = list()
for(simp in c(T, F)){
  n_use = names(exp_list)[grepl("simp", names(exp_list))==simp]
  #ct_int_exp = cbind(exp_list[[n_use[1]]], exp_list[[n_use[3]]]$value, exp_list[[n_use[3]]]$value)
  ct_int_exp = merge(exp_list[[n_use[1]]], exp_list[[n_use[2]]], by = 1:6)
  ct_int_exp = merge(ct_int_exp, exp_list[[n_use[3]]], by = 1:6)
  ct_int_exp$value.x[is.na(ct_int_exp$value.x)] = ct_int_exp$value.y[is.na(ct_int_exp$value.y)] = ct_int_exp$value[is.na(ct_int_exp$value)] = 0
  colnames(ct_int_exp)[7:9] = c("healthy_exp", "embolised_exp", "regenerating_exp")
  ct_int_exp = ct_int_exp[ct_int_exp$healthy_exp>0 | 
                            ct_int_exp$embolised_exp>0 | 
                            ct_int_exp$regenerating_exp>0,]

  tup_list = list()
  keep_row = c()
  for(i in 1:nrow(ct_int_exp)){
    tup1 = paste(c(ct_int_exp$gene[i], ct_int_exp$gene_target[i], ct_int_exp$ct[i], 
                   ct_int_exp$ct_target[i], ct_int_exp$cond[i]), collapse = " ")
    tup2 = paste(c(ct_int_exp$gene_target[i], ct_int_exp$gene[i], ct_int_exp$ct_target[i], 
                   ct_int_exp$ct[i], ct_int_exp$cond[i]), collapse = " ")
    if(!(tup1 %in% tup_list) & !(tup2 %in% tup_list)){
      tup_list = c(tup_list, tup1, tup2)
      keep_row = c(keep_row, T)
    } else{
      keep_row = c(keep_row, F)
    }
  }
  ct_int_exp = ct_int_exp[keep_row,]
  
  ct_int_exp = merge(ct_int_exp, unique(ct_g_cond_ann[,c(1,4)]), by = 1, all = T)
  nn = if(simp) "simp" else "all"
  ct_int_exp_l[[nn]] = ct_int_exp
  
  ct_int_exp_l[[nn]]$cond = unlist(lapply(strsplit(ct_int_exp_l[[nn]], "_"), function(x) x[1]))
}
Error in strsplit(ct_int_exp_l[[nn]], "_") : non-character argument

GSEA of interactions using mutual information

inter_annot = read.csv("data/interaction_annotation2.csv", header = T)
inter_annot$funct[grepl("imm", inter_annot$funct)] = "immune"
inter_annot$funct[grepl("inflam", inter_annot$funct)] = "immune"
gr = inter_annot$funct
names(gr) = inter_annot$inter
gr_list = tapply(inter_annot$inter, inter_annot$funct, function(x) x)

her_allint_l = list()
for(simp in c(T, F)){
  n_use = names(reform_list)[grepl("simp", names(reform_list))==simp]
  
  he_allint = merge(reform_list[[n_use[1]]][,1:6], reform_list[[n_use[2]]][,1:6], by = 1:3, all = T)
  he_allint$lr1.x[is.na(he_allint$lr1.x)] = he_allint$lr1.y[is.na(he_allint$lr1.x)]
  he_allint$lr2.x[is.na(he_allint$lr2.x)] = he_allint$lr2.y[is.na(he_allint$lr2.x)]
  he_allint = he_allint[,c(1:6,9)]
  her_allint = merge(he_allint, reform_list[[n_use[3]]][,1:6], by = 1:3, all = T)
  her_allint$lr1.x[is.na(her_allint$lr1.x)] = her_allint$lr1[is.na(her_allint$lr1.x)]
  her_allint$lr2.x[is.na(her_allint$lr2.x)] = her_allint$lr2[is.na(her_allint$lr2.x)]
  her_allint = her_allint[,c(1:7,10)]
  her_allint[is.na(her_allint)] = 0
  colnames(her_allint)[4:8] = c("lr1", "lr2", "healthy_exp", "embolised_exp", "regenerating_exp")
  
  # count occurrences per condition. this works bc we're already working with sig means
  her_allint = merge(her_allint, data.frame(table(her_allint$id_cp_interaction[her_allint$healthy_exp>0])), 
                                            by = 1, all.x = T)
  her_allint = merge(her_allint, data.frame(table(her_allint$id_cp_interaction[her_allint$embolised_exp>0])), 
                                            by = 1, all.x = T)
  her_allint = merge(her_allint, 
                     data.frame(table(her_allint$id_cp_interaction[her_allint$regenerating_exp>0])), 
                     by = 1, all.x = T)
  colnames(her_allint)[9:11] = c("healthy_n", "embolised_n", "regenerating_n")
  her_allint[is.na(her_allint)] = 0
  her_allint$ct_pair = factor(paste0(her_allint$ct1, "_", her_allint$ct2))
  
  comb_cond = combn(colnames(her_allint)[6:8],2)
  colnames(comb_cond) = c("he", "hr", "er")
  for(i in colnames(comb_cond)){
    plot_df = her_allint[her_allint[,comb_cond[1,i]]>0 | her_allint[,comb_cond[2,i]]>0,1:12]
    
    exp_df1 = reshape2::dcast(plot_df, formula = id_cp_interaction ~ ct_pair, 
                              value.var = comb_cond[1,i], fill = 0)
    rownames(exp_df1) = exp_df1[,1]
    exp_df1 = exp_df1[,-1]>0
    exp_df2 = reshape2::dcast(plot_df, formula = id_cp_interaction ~ ct_pair, 
                              value.var = comb_cond[2,i], fill = 0)
    rownames(exp_df2) = exp_df2[,1]
    exp_df2 = exp_df2[,-1]>0
    
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) infotheo::mutinformation(exp_df1[x,], exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) e1071::hamming.distance(exp_df1[x,], exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) e1071::hamming.distance(exp_df1[x,],
                                                                        exp_df2[x,])/sum(exp_df1[x,] |
                                                                                           exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) sum(exp_df1[x,] | exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    
    colnames(plot_df)[13:16] = paste0(c("mutInfo_", "hamm_", "hammNorm_", "tot_"), i)
    
    her_allint = merge(her_allint, unique(plot_df[,c(1,13:16)]), all.x = T, by = 1)
  }
  her_allint$mutInfo_er[is.na(her_allint$mutInfo_er)] = 1
  her_allint$mutInfo_hr[is.na(her_allint$mutInfo_hr)] = 1
  her_allint$mutInfo_he[is.na(her_allint$mutInfo_he)] = 1
  her_allint$tot_he[is.na(her_allint$tot_he)] = 0
  her_allint$tot_hr[is.na(her_allint$tot_hr)] = 0
  her_allint$tot_er[is.na(her_allint$tot_er)] = 0
  her_allint$hamm_er[is.na(her_allint$hamm_er)] = 0
  her_allint$hamm_hr[is.na(her_allint$hamm_hr)] = 0
  her_allint$hamm_he[is.na(her_allint$hamm_he)] = 0
  her_allint$hammNorm_er[is.na(her_allint$hammNorm_er)] = 0
  her_allint$hammNorm_he[is.na(her_allint$hammNorm_he)] = 0
  her_allint$hammNorm_hr[is.na(her_allint$hammNorm_hr)] = 0
  
  her_allint$diff_n_he = her_allint$embolised_n-her_allint$healthy_n
  her_allint$diff_n_hr = her_allint$regenerating_n-her_allint$healthy_n
  her_allint$diff_n_er = her_allint$regenerating_n-her_allint$embolised_n
  
  # plot tot vs mutual
  plot_df = unique(her_allint[,c("id_cp_interaction", paste0(c("mutInfo_", "tot_", "diff_n_"), i))])
  plt = ggplot(plot_df, aes(x = tot_er, y = mutInfo_er*(diff_n_er/abs(diff_n_er))))+
    geom_bin2d()+
    scale_x_log10()+
    theme_bw()+
    theme(aspect.ratio = 1)
  print(plt)
  
  nn = if(simp) "simp" else "all"
  her_allint_l[[nn]] = her_allint
}
Aggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
column names ‘y.x’, ‘y.y’ are duplicated in the resultAggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
column names ‘y.x’, ‘y.y’ are duplicated in the resultAggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
column names ‘y.x’, ‘y.y’ are duplicated in the result

saveRDS(her_allint_l, file = "./results/cell_comm/updt/interactions_mutInfo_condComp.RDS")

Save mutual information for LR and cell types

for(n in names(her_allint_l)){
  her_allint = her_allint_l[[n]]
  # select genes from lowest mutual (table - get most common)
  # +
  # mean mutual per cell type (lowest = more change)
  ## plot interactions based on those genes (some are involved in more than one)
  ## heatmap - rows ct; columns genes; gaps between interact; 1 heatmap/cond, same ct ordering
  sub_df1 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","ct1","mutInfo_he")])
  sub_df2 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","ct2","mutInfo_he")])
  ct_mut_he = tapply(c(sub_df1$mutInfo_he, sub_df2$mutInfo_he), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","ct1","mutInfo_hr")])
  sub_df2 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","ct2","mutInfo_hr")])
  ct_mut_hr = tapply(c(sub_df1$mutInfo_hr, sub_df2$mutInfo_hr), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","ct1","mutInfo_er")])
  sub_df2 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","ct2","mutInfo_er")])
  ct_mut_er = tapply(c(sub_df1$mutInfo_er, sub_df2$mutInfo_er), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  ct_mut_df = cbind(ct_mut_he,ct_mut_hr,ct_mut_er)
  rownames(ct_mut_df) = names(ct_mut_he)
  colnames(ct_mut_df) = c("he", "hr", "er")
  saveRDS(ct_mut_df, file = "./results/cell_comm/updt/ct_select_mutInfo_condComp.RDS")
  
  sub_df1 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","lr1","mutInfo_he")])
  sub_df2 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","lr2","mutInfo_he")])
  lr_mut_he = tapply(c(sub_df1$mutInfo_he, sub_df2$mutInfo_he), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","lr1","mutInfo_hr")])
  sub_df2 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","lr2","mutInfo_hr")])
  lr_mut_hr = tapply(c(sub_df1$mutInfo_hr, sub_df2$mutInfo_hr), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","lr1","mutInfo_er")])
  sub_df2 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","lr2","mutInfo_er")])
  lr_mut_er = tapply(c(sub_df1$mutInfo_er, sub_df2$mutInfo_er), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  
  lr_cnt_he = table(c(her_allint[her_allint$tot_he>0 & her_allint$mutInfo_he<=0.05,"lr1"],
                      her_allint[her_allint$tot_he>0 & her_allint$mutInfo_he<=0.05,"lr2"]))
  lr_he = merge(lr_cnt_he, lr_mut_he, by.x = 1, by.y = 0)
  lr_cnt_hr = table(c(her_allint[her_allint$tot_hr>0 & her_allint$mutInfo_hr<=0.05,"lr1"],
                      her_allint[her_allint$tot_hr>0 & her_allint$mutInfo_hr<=0.05,"lr2"]))
  lr_hr = merge(lr_cnt_hr, lr_mut_hr, by.x = 1, by.y = 0)
  lr_cnt_er = table(c(her_allint[her_allint$tot_er>0 & her_allint$mutInfo_er<=0.05,"lr1"],
                      her_allint[her_allint$tot_er>0 & her_allint$mutInfo_er<=0.05,"lr2"]))
  lr_er = merge(lr_cnt_er, lr_mut_er, by.x = 1, by.y = 0)
  
  # ECM - which proteins/collagens?; mention TGFB
  # dev - which ligands/receptors
  lr_all = rbind(lr_he, lr_hr, lr_er)
  lr_all$cond = c(rep("he", nrow(lr_he)), rep("hr", nrow(lr_hr)),rep("er", nrow(lr_er)))
  colnames(lr_all) = c("lr", "n_mut.05", "mean_mutInfo", "cond")
  saveRDS(lr_all, file = paste0("./results/cell_comm/updt/", n, "LR_select_mutInfo_condComp.RDS"))
}

Plot interactions

for(n in names(ct_int_exp_l)){
  ct_int_exp = ct_int_exp_l[[n]]
  r = if(n=="all") 1:3 else 4:6
  
  # interactions
  intdf = unique(Reduce(rbind, reform_list[r])[,c(1,4,5)])
  intdf$intpair = paste0(intdf$lr1, " - ", intdf$lr2)
  
  ct_int_exp_file = unique(ct_int_exp[,c(1,4:5,7:10)])
  ct_int_exp_file$ctpair = paste0(ct_int_exp_file$ct, " - ", ct_int_exp_file$ct_target)
  
  plot_df_int = reshape2::melt(ct_int_exp_file[,c(1,8,4:7)])
  plot_df_int$variable = unlist(lapply(strsplit(as.character(plot_df_int$variable), "_"), 
                                       function(x) x[[1]][1]))
  plot_df_int$variable = factor(plot_df_int$variable, 
                                levels = rev(c("healthy", "embolised", "regenerating")))
  
  sub_plot_df_int = plot_df_int[grepl("Stellate", plot_df_int$ctpair) |
                                  grepl("Kupffer", plot_df_int$ctpair) |
                                  grepl("LSEC", plot_df_int$ctpair),]
  
  sub_plot_df_int = merge(sub_plot_df_int, intdf[,c(1,4)], by = 1, all.x = T)
  
  gg = "ECM"
  plt = ggplot(sub_plot_df_int[sub_plot_df_int$description==gg,], 
         aes(x = ctpair, y = variable, colour = value, size = value))+
    facet_grid(intpair~.)+
    guides(size = guide_legend(title = "exp", reverse = T), 
           colour = guide_legend(title = "exp", reverse = T))+
    geom_point()+
    labs(title = gg)+
    theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1),
          strip.text.y = element_text(angle = 0, size = 8))
  print(plt)
}

Important tables

for(n in names(inter_df)){
  write.csv(inter_df[[n]], col.names = T, row.names = F, quote = F,
            file = paste0("results/cell_comm/updt/tables/Interact_", n, "_celltypes_cond.csv"))
}
for(n in names(ct_int_exp_l)){
  ct_int_exp = ct_int_exp_l[[n]]
  write.csv(ct_int_exp, col.names = T, row.names = F, quote = F, 
            file = paste0("results/cell_comm/updt/tables/", n, "interact_celltype_exp_group.csv"))
}

Cell-cell communication networks

Load data to make cell comm networks (NOT USED HERE)

redone_meta = list()
for(cc in unique(allcells_css$Condition)){
  redone_meta[[cc]] = read.table(paste0("results/cell_comm/CellPhoneDB_runs_updt/", 
                                        cc, "/", cc, "_meta_names.txt"), 
                                 sep = "\t", header = T, row.names = 1)
}
redone_meta_all = Reduce(rbind, redone_meta)

allcells_redone = AddMetaData(allcells_css, redone_meta_all)
allcells_redone = allcells_redone[,!is.na(allcells_redone$cell_type)]

Functions used to make cell comm networks

makeMedian = function(point_df, edge_df, cl = c("ct2", "maj_g1", "maj_g2")){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  maj_mat = table(edge_df[,cl[2]], edge_df[,cl[3]])
  diag(maj_mat) = 0
  edge_major = data.frame(maj_mat + t(maj_mat))
  edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
  edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
  
  clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
  keep = c()
  for(j in 1:ncol(clcomb)){
    keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
  }
  edge_major = edge_major[keep,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

makeMedianCond = function(point_df, edge_df, cl = c("ct", "ct_g1", "ct_g2"), edge_by = "condition"){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  edge_l = list()
  for(i in unique(edge_df[,edge_by])){
    sub_edge_df = edge_df[edge_df[,edge_by]==i,]
    maj_mat = table(sub_edge_df[,cl[2]], sub_edge_df[,cl[3]])
    diag(maj_mat) = 0
    edge_major = data.frame(maj_mat + t(maj_mat))
    edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
    edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
    
    # remove repeated
    clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
    keep = c()
    for(j in 1:ncol(clcomb)){
      keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
    }
    
    edge_l[[i]] = edge_major[keep,]
  }
  edge_major = Reduce(rbind, edge_l)
  edge_major[,edge_by] = unlist(lapply(names(edge_l), function(x) rep(x, nrow(edge_l[[x]]))))
  edge_major = edge_major[edge_major$Var2!=edge_major$Var1,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

Plot ligands and receptors with MDS

makeMedian = function(point_df, edge_df, cl = c("ct2", "maj_g1", "maj_g2")){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  maj_mat = table(edge_df[,cl[2]], edge_df[,cl[3]])
  diag(maj_mat) = 0
  edge_major = data.frame(maj_mat + t(maj_mat))
  edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
  edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
  
  clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
  keep = c()
  for(j in 1:ncol(clcomb)){
    keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
  }
  edge_major = edge_major[keep,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

makeMedianCond = function(point_df, edge_df, cl = c("ct", "ct_g1", "ct_g2"), edge_by = "condition"){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  edge_l = list()
  for(i in unique(edge_df[,edge_by])){
    sub_edge_df = edge_df[edge_df[,edge_by]==i,]
    maj_mat = table(sub_edge_df[,cl[2]], sub_edge_df[,cl[3]])
    diag(maj_mat) = 0
    edge_major = data.frame(maj_mat + t(maj_mat))
    edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
    edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
    
    # remove repeated
    clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
    keep = c()
    for(j in 1:ncol(clcomb)){
      keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
    }
    
    edge_l[[i]] = edge_major[keep,]
  }
  edge_major = Reduce(rbind, edge_l)
  edge_major[,edge_by] = unlist(lapply(names(edge_l), function(x) rep(x, nrow(edge_l[[x]]))))
  edge_major = edge_major[edge_major$Var2!=edge_major$Var1,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

Save network objects

inter_df = readRDS(file = "results/cell_comm/updt/cond_diff_interact_DE.RDS")

# interactions unique to each condition
unique_inters = c(setdiff(inter_df$healthy$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                  setdiff(inter_df$embolised$id_cp_interaction, 
                          c(inter_df$healthy$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                  setdiff(inter_df$regenerating$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$healthy$id_cp_interaction)))

# interactions unique to healthy or to emb/regen
comph_inters = c(setdiff(inter_df$healthy$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                 setdiff(inter_df$embolised$id_cp_interaction, inter_df$healthy$id_cp_interaction),
                 setdiff(inter_df$regenerating$id_cp_interaction, inter_df$healthy$id_cp_interaction))

# prepare gene pairs per condition
gene_pairs_cond = rbind(unique(inter_df$healthy[,c("id_cp_interaction", "gn1", "gn2")]),
                        unique(inter_df$embolised[,c("id_cp_interaction", "gn1", "gn2")]),
                        unique(inter_df$regenerating[,c("id_cp_interaction", "gn1", "gn2")]))
gene_pairs_cond$condition = c(rep("healthy", nrow(unique(inter_df$healthy[,c("id_cp_interaction", 
                                                                             "gn1", "gn2")]))),
                              rep("embolised", nrow(unique(inter_df$embolised[,c("id_cp_interaction", 
                                                                                 "gn1", "gn2")]))),
                              rep("regenerating", nrow(unique(inter_df$regenerating[,c("id_cp_interaction",
                                                                                       "gn1", "gn2")]))))

# list all LR genes
all_lr_genes = unique(c(as.character(inter_df$healthy$gn1), as.character(inter_df$healthy$gn2),
                        as.character(inter_df$embolised$gn1), as.character(inter_df$embolised$gn2),
                        as.character(inter_df$regenerating$gn1), as.character(inter_df$regenerating$gn2)))
all_lr_genes = all_lr_genes[all_lr_genes %in% rownames(sub_allcells_css@assays$SCT@data)]

# calculate mean per cell type and condition for each LR gene
mean_exp_cond_lr = apply(sub_allcells_css@assays$SCT@data[all_lr_genes,], 1, 
                         function(x) tapply(x, paste0(sub_allcells_css$subpops, 
                                                      "_", sub_allcells_css$Condition), mean))

# determine the cell type and condition with the highest expression
max_cond_ct = rownames(mean_exp_cond_lr)[apply(mean_exp_cond_lr, 2, which.max)]
names(max_cond_ct) = colnames(mean_exp_cond_lr)
max_cond_ct_ct = unlist(lapply(strsplit(max_cond_ct, "_"), function(x) x[1]))
max_cond_ct_cond = unlist(lapply(strsplit(max_cond_ct, "_"), function(x) x[2]))

# correlation of mean expression
cor_cond_lr = cor(mean_exp_cond_lr, method = "sp")

# filter correlation with itself, keep only genes with cor>=0.3
diag(cor_cond_lr) = 0
adj_cond_mat = cor_cond_lr>=0.3

# build graph, project with MDS
network_cond = graph_from_adjacency_matrix(adj_cond_mat, weighted=T, mode="undirected", diag=F)
l_cond = igraph::layout_with_mds(network_cond)
l_cond = data.frame(l_cond)
l_cond$gene = colnames(adj_cond_mat)
rownames(l_cond) = colnames(adj_cond_mat)

# define all edges, based on CellPhoneDB pairings
tmp_df = merge(gene_pairs_cond, l_cond, by.x = "gn1", by.y = "gene")
edge_cond_df = merge(tmp_df, l_cond, by.x = "gn2", by.y = "gene")
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_ct), by.x = 1, by.y = 0, all.x = T)
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_ct), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_df)[9:10] = c("ct_g1", "ct_g2")
# add highest expressing major cell types
edge_cond_df$maj_g1 = ifelse(grepl("LSEC", edge_cond_df$ct_g1), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_df$ct_g1), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_df$ct_g1), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_df$ct_g1), "Cholangiocytes", 
                                           "Immune"))))
edge_cond_df$maj_g2 = ifelse(grepl("LSEC", edge_cond_df$ct_g2), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_df$ct_g2), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_df$ct_g2), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_df$ct_g2), "Cholangiocytes", 
                                           "Immune"))))
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_cond), by.x = 1, by.y = 0, all.x = T)
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_cond), by.x = 2, by.y = 0, all.x = T)

# define the vertices of the network
point_cond_df = l_cond
point_cond_df$ct = max_cond_ct_ct[point_cond_df$gene]
point_cond_df$ct2 = ifelse(grepl("LSEC", point_cond_df$ct), "Endothelial",
                      ifelse(grepl("Hepatocytes", point_cond_df$ct), "Hepatocytes",
                             ifelse(grepl("Stellate cells", point_cond_df$ct), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", point_cond_df$ct), "Cholangiocytes", 
                                           "Immune"))))
point_cond_df$cond = max_cond_ct_cond[point_cond_df$gene]

# define the median points for each cell type (using max expression)
pe_l = makeMedian(point_cond_df, edge_cond_df, cl = c("ct", "ct_g1", "ct_g2"))

# plot total gene correlation projection and median network
pltboth = ggplot()+
  geom_point(data = point_cond_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.25, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_segment(data = pe_l[[2]], 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq), 
               alpha = 0.15, show.legend = F)+
  geom_point(data = pe_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


pltboth = ggplot()+
  geom_segment(data = edge_cond_df, 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y), 
               alpha = 0.03, show.legend = F)+
  geom_point(data = point_cond_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.6, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_point(data = pe_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


# get median per cell type, per condition - FULL NETWORK
pe_cond_l = makeMedianCond(point_cond_df, edge_cond_df, cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l[[2]][pe_cond_l[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, 
                               size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l[["leg"]] = cowplot::get_legend(plt_cond_l[["healthy"]])

# plot FULL NETWORK median per condition
cowplot::plot_grid(plt_cond_l[[1]]+theme(legend.position = "none"), 
                   plt_cond_l[[2]]+theme(legend.position = "none"),
                   plt_cond_l[[3]]+theme(legend.position = "none"), plt_cond_l$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - UNIQUE PER CONDTION NETWORK
pe_cond_l_u = makeMedianCond(point_cond_df, edge_cond_df[edge_cond_df$id_cp_interaction %in% unique_inters,],
                             cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_u = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_u[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_u[[2]][pe_cond_l_u[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_u[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_u[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_u[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_u[["leg"]] = cowplot::get_legend(plt_cond_l_u[["healthy"]])

# plot UNIQUE PER CONDTION NETWORK median per condition
cowplot::plot_grid(plt_cond_l_u[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_u[[2]]+theme(legend.position = "none"),
                   plt_cond_l_u[[3]]+theme(legend.position = "none"), plt_cond_l_u$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - HEALTHY NETWORK
pe_cond_l_h = makeMedianCond(point_cond_df, edge_cond_df[edge_cond_df$id_cp_interaction %in% inter_df$healthy$id_cp_interaction,], cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_h = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_h[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_h[[2]][pe_cond_l_h[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_h[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_h[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_h[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_h[["leg"]] = cowplot::get_legend(plt_cond_l_h[["healthy"]])

# plot HEALTHY NETWORK median per condition
cowplot::plot_grid(plt_cond_l_h[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_h[[2]]+theme(legend.position = "none"),
                   plt_cond_l_h[[3]]+theme(legend.position = "none"), plt_cond_l_h$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - HEALTHY COMPARISON NETWORK
pe_cond_l_ch = makeMedianCond(point_cond_df, 
                             edge_cond_df[edge_cond_df$id_cp_interaction %in% comph_inters,], 
                             cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_ch = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_ch[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_ch[[2]][pe_cond_l_ch[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_ch[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_ch[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_ch[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_ch[["leg"]] = cowplot::get_legend(plt_cond_l_ch[["healthy"]])

# plot HEALTHY COMPARISON NETWORK median per condition
cowplot::plot_grid(plt_cond_l_ch[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_ch[[2]]+theme(legend.position = "none"),
                   plt_cond_l_ch[[3]]+theme(legend.position = "none"), plt_cond_l_ch$leg, 
                   ncol = 2, rel_widths = c(1,1,1,0.5))

Plot ligands and receptors with UMAP

save(edge_cond_df, point_cond_df, file = "results/cell_comm/updt/networks_cond.RData")
save(pe_l, pe_cond_l, pe_cond_l_u, pe_cond_l_h, pe_cond_l_ch, 
     file = "results/cell_comm/updt/median_networks_cond.RData")

Save UMAP network objects

set.seed(2954)
l = uwot::umap(t(mean_exp_cond_lr), metric = "cosine", ret_nn = T, n_epochs = 1000)
l_cond = data.frame(l$embedding)
l_cond$gene = colnames(mean_exp_cond_lr)
rownames(l_cond) = colnames(mean_exp_cond_lr)
tmp_df = merge(gene_pairs_cond, l_cond, by.x = "gn1", by.y = "gene")
edge_cond_umap_df = merge(tmp_df, l_cond, by.x = "gn2", by.y = "gene")
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_ct), by.x = 1, by.y = 0, all.x = T)
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_ct), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_umap_df)[9:10] = c("ct_g1", "ct_g2")
edge_cond_umap_df$maj_g1 = ifelse(grepl("LSEC", edge_cond_umap_df$ct_g1), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_umap_df$ct_g1), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_umap_df$ct_g1), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_umap_df$ct_g1),
                                           "Cholangiocytes", "Immune"))))
edge_cond_umap_df$maj_g2 = ifelse(grepl("LSEC", edge_cond_umap_df$ct_g2), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_umap_df$ct_g2), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_umap_df$ct_g2), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_umap_df$ct_g2),
                                           "Cholangiocytes", "Immune"))))
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_cond), by.x = 1, by.y = 0, all.x = T)
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_cond), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_umap_df)[4] = "cond"

point_cond_umap_df = l_cond
point_cond_umap_df$ct = max_cond_ct_ct[point_cond_umap_df$gene]
point_cond_umap_df$ct2 = ifelse(grepl("LSEC", point_cond_umap_df$ct), "Endothelial",
                      ifelse(grepl("Hepatocytes", point_cond_umap_df$ct), "Hepatocytes",
                             ifelse(grepl("Stellate cells", point_cond_umap_df$ct), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", point_cond_umap_df$ct), "Cholangiocytes", 
                                           "Immune"))))
point_cond_umap_df$cond = max_cond_ct_cond[point_cond_umap_df$gene]


pe_umap_l = makeMedian(point_cond_umap_df, edge_cond_umap_df, cl = c("ct", "ct_g1", "ct_g2"))

# plot total gene correlation projection and median network
pltboth = ggplot()+
  geom_point(data = point_cond_umap_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.25, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_segment(data = pe_umap_l[[2]], 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq), 
               alpha = 0.15, show.legend = F)+
  geom_point(data = pe_umap_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


pltboth = ggplot()+
  geom_segment(data = edge_cond_umap_df, 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y), 
               alpha = 0.03, show.legend = F)+
  geom_point(data = point_cond_umap_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.6, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_point(data = pe_umap_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


# get median per cell type, per condition - HEALTHY COMPARISON NETWORK
pe_umap_cond_l_ch = makeMedianCond(point_cond_umap_df, 
                                   edge_cond_umap_df[edge_cond_umap_df$id_cp_interaction%in%comph_inters,],
                                   edge_by = "cond", cl = c("ct", "ct_g1", "ct_g2"))

plt_umap_cond_l_ch = list()
for(cc in unique(pe_cond_l[[2]]$cond)){
  plt_umap_cond_l_ch[[cc]] = ggplot()+
    geom_segment(data = pe_umap_cond_l_ch[[2]][pe_umap_cond_l_ch[[2]]$cond==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_umap_cond_l_ch[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_umap_cond_l_ch[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_umap_cond_l_ch[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_umap_cond_l_ch[["leg"]] = cowplot::get_legend(plt_umap_cond_l_ch[["healthy"]])

# plot HEALTHY COMPARISON NETWORK median per condition
cowplot::plot_grid(plt_umap_cond_l_ch[[1]]+theme(legend.position = "none"), 
                   plt_umap_cond_l_ch[[2]]+theme(legend.position = "none"),
                   plt_umap_cond_l_ch[[3]]+theme(legend.position = "none"), plt_umap_cond_l_ch$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))

LS0tCnRpdGxlOiAiQ2VsbCBjb21tdW5pY2F0aW9uIGFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCiMgR2VuZXJhbCBTZXR1cApTZXR1cCBjaHVuawoKYGBge3IsIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gOCkKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBub3JtYWxpemVQYXRoKCIuLiIpKQprbml0cjo6b3B0c19rbml0JGdldCgicm9vdC5kaXIiKQpgYGAKClNldHVwIHJldGljdWxhdGUKCmBgYHtyfQpsaWJyYXJ5KHJldGljdWxhdGUpCmtuaXRyOjprbml0X2VuZ2luZXMkc2V0KHB5dGhvbiA9IHJldGljdWxhdGU6OmVuZ19weXRob24pCnB5X2F2YWlsYWJsZShpbml0aWFsaXplID0gRkFMU0UpCnVzZV9weXRob24oU3lzLndoaWNoKCJweXRob24iKSkKcHlfY29uZmlnKCkKYGBgCgpMb2FkIGxpYnJhcmllcwoKYGBge3J9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KGRhdGEudGFibGUpCmBgYAoKCiMgTG9hZCBhbmQgcHJlcHJvY2VzcyBkYXRhCkxvYWQgZGF0YSAoZnJvbSBhbGwgY2VsbHMpCgpgYGB7cn0KYWxsY2VsbHNfY3NzID0gcmVhZFJEUyhmaWxlID0gImRhdGEvcHJvY2Vzc2VkL2FsbGNlbGxzX2Nzcy5SRFMiKQplbmRfY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9lbmRvdGhlbGlhbC9vbmx5X2VuZF9jZWxsc196b24uUkRTIikKaGVwX2NlbGxzID0gcmVhZFJEUyhmaWxlID0gInJlc3VsdHMvem9uYXRpb25fY29uZC9oZXBfY2VsbHNfem9uYXRpb25fcmFuay5SRFMiKQppbW1fY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9pbW11bmUvYWxsX2ltbV9jZWxscy5SRFMiKQpgYGAKClByZXBhcmUgYSBnbG9iYWwgb2JqZWN0IHdpdGggYWxsIG5lY2Vzc2FyeSBtZXRhZGF0YQoKYGBge3J9CmVuZHBvcF9kZiA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXMoZW5kX2NlbGxzKSwKICAgICAgICAgICAgICAgICAgICAgICAic3VicG9wcyIgPSBlbmRfY2VsbHNAbWV0YS5kYXRhJGVuZG9fc2ltcCkKaW1tcG9wX2RmID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBjb2xuYW1lcyhpbW1fY2VsbHMpLAogICAgICAgICAgICAgICAgICAgICAgICJzdWJwb3BzIiA9IGltbV9jZWxsc0BtZXRhLmRhdGEkaW1tdW5lX2Fubm90KQpoZXBwb3BfZGYgPSBsYXBwbHkoaGVwX2NlbGxzLCBmdW5jdGlvbih4KSBjYmluZChjb2xuYW1lcyh4KSwgYXMuY2hhcmFjdGVyKHgkem9uYXRpb25faW50KSkpCmhlcHBvcF9kZiA9IFJlZHVjZShyYmluZCwgaGVwcG9wX2RmKQpoZXBwb3BfZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGhlcHBvcF9kZlssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgc3VicG9wcyA9IGZhY3RvcihoZXBwb3BfZGZbLDJdKSkKbGV2ZWxzKGhlcHBvcF9kZiRzdWJwb3BzKSA9IGMoIigtMC4wMDA5OSwwLjMzM10iID0gIkhlcGF0b2N5dGVzX1oxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIigwLjMzMywwLjY2N10iID0gIkhlcGF0b2N5dGVzX1oyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIigwLjY2NywxXSIgPSAiSGVwYXRvY3l0ZXNfWjMiKQpoZXBwb3BfZGYkc3VicG9wcyA9IGFzLmNoYXJhY3RlcihoZXBwb3BfZGYkc3VicG9wcykKCnN1YnBvcF9kZiA9IHJiaW5kKGVuZHBvcF9kZiwgaW1tcG9wX2RmLCBoZXBwb3BfZGYpCnN1YnBvcF9kZiRzdWJwb3BzW3N1YnBvcF9kZiRzdWJwb3BzPT0iQ3ljbGluZyBjZWxscyJdID0gIkRpdmlkaW5nIGVuZG90aGVsaWFsIGNlbGxzIgoKIyBhZGQgc3VicG9wIG1ldGFkYXRhCmFsbGNlbGxzX2NzcyA9IEFkZE1ldGFEYXRhKGFsbGNlbGxzX2Nzcywgc3VicG9wX2RmKQphbGxjZWxsc19jc3Mkc3VicG9wc1tpcy5uYShhbGxjZWxsc19jc3Mkc3VicG9wcyldID0gYWxsY2VsbHNfY3NzJGFsbGNlbGxzX21ham9yW2lzLm5hKGFsbGNlbGxzX2NzcyRzdWJwb3BzKV0KYWxsY2VsbHNfY3NzJHN1YnBvcHNbYWxsY2VsbHNfY3NzJHN1YnBvcHM9PSJIZXBhdG9jeXRlLU1vbm9jeXRlIGludGVyYWN0aW9uIl0gPSAiRG91YmxldHMiCgojIHRoZSBjZWxscyBpbiB0aGlzIG9iamVjdCBuYW1lZCAiSGVwYXRvY3l0ZXMiLCAiRW5kb3RoZWxpYWwgY2VsbHMiLCBhbmQgIkRvdWJsZXRzIiBoYXZlIHRvIGJlIHJlbW92ZWQKIyMgdGhlIGZpcnN0IHR3byBhcmUgY2VsbHMgdGhhdCBkaWRuJ3QgcGFzcyB0aGUgaGVwIGFuZCBlbmQgYW5hbHlzaXMKc3ViX2FsbGNlbGxzX2NzcyA9IGFsbGNlbGxzX2Nzc1ssIShhbGxjZWxsc19jc3Mkc3VicG9wcyAlaW4lIGMoIkhlcGF0b2N5dGVzIiwgIkVuZG90aGVsaWFsIGNlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRvdWJsZXRzIikpXQoKIyBhZGQgc2ltcGxpZmllZCBjb2x1bW4gLSBtZXJnZSBzb21lIHBvcHVsYXRpb25zLCBkb24ndCBpbmNsdWRlIGN5Y2xpbmcgcG9wcyBhbmQgc3RyZXNzZWQgVCBjZWxscwpzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcCA9IHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wcwpzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcFtncmVwbCgiTUFJVCIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wKV0gPSAiTUFJVCBjZWxscyIKc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXBbZ3JlcGwoIk5LIGNlbGxzICIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wKV0gPSAiTksgY2VsbHMiCnN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wW2dyZXBsKCJMU0VDIChoaWdoIE1UIiwgc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpXSA9ICJMU0VDIChoaWdoIE1UKSIKc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXBbZ3JlcGwoIkNEOCBhYi1UICIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKV0gPSAiQ0Q4IGFiLVQgY2VsbHMiCnNpbXBfYWxsY2VsbHNfY3NzID0gc3ViX2FsbGNlbGxzX2Nzc1ssIShzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcCAlaW4lIGMoImFiLVQgY2VsbHMgKHN0cmVzcykiLCAiRGl2aWRpbmcgY0RDcyIsICJEaXZpZGluZyBlbmRvdGhlbGlhbCBjZWxscyIsIkRpdmlkaW5nIFQvTksgY2VsbHMiKSldCmBgYAoKU3Vic2V0IGFuZCBwcm9jZXNzIGVhY2ggY29uZGl0aW9uCgpgYGB7cn0KI2NwZGJfcGF0aCA9ICJyZXN1bHRzL2NlbGxfY29tbS9DZWxsUGhvbmVEQl9ydW5zX3VwZHQiCmNwZGJfcGF0aCA9ICIvbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvQ2VsbFBob25lREJfcnVuc191cGR0IgoKY29uZF9jZWxscyA9IGxpc3QoKQpmb3IoY29uZCBpbiB1bmlxdWUoc3ViX2FsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uKSl7CiAgIyBzdWJzZXQgY29uZGl0aW9uCiAgY29uZF9jZWxsc1tbY29uZF1dID0gc3ViX2FsbGNlbGxzX2Nzc1ssc3ViX2FsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uPT1jb25kXQogIAogICMgZGVmaW5lIG1ldGFkYXRhCiAgbWV0YSA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXMoY29uZF9jZWxsc1tbY29uZF1dQG1ldGEuZGF0YSksCiAgICAgICAgICAgICAgICAgICAgIkNlbGwiID0gcm93bmFtZXMoY29uZF9jZWxsc1tbY29uZF1dQG1ldGEuZGF0YSksIAogICAgICAgICAgICAgICAgICAgICJjZWxsX3R5cGUiID0gYXMuY2hhcmFjdGVyKGNvbmRfY2VsbHNbW2NvbmRdXUBtZXRhLmRhdGEkc3VicG9wcyksCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgd3JpdGUudGFibGUobWV0YSwgZmlsZSA9IHBhc3RlMChjcGRiX3BhdGgsICIvIiwgY29uZCwgIi8iLCBjb25kLCAiX21ldGFfbmFtZXMudHh0IiksIAogICAgICAgICAgICBzZXAgPSAiXHQiLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCiAgCiAgIyBub3JtYWxpc2UgYW5kIHNhdmUKICBjb25kX2NlbGxzW1tjb25kXV0gPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGNvbmRfY2VsbHNbW2NvbmRdXSwgZG8uY29ycmVjdC51bWkgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJ1bmlxdWVfbmFtZSIsICJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5ydi50aCA9IDEsIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIHZlcmJvc2UgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQogIAogIGRhdCA9IGNiaW5kKHJvd25hbWVzKGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpLAogICAgICAgICAgICAgIE1hdHJpeDo6YXMubWF0cml4KGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpKQogIGNvbG5hbWVzKGRhdClbMV0gPSAiR2VuZSIKICB3cml0ZS50YWJsZShkYXQsIGZpbGUgPSBwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIGNvbmQsICIvIiwgY29uZCwgIl9leHBfbm9ybS50eHQiKSwgCiAgICAgICAgICAgICAgc2VwID0gIlx0IiwgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGKQogIAogIAogICMgc3Vic2V0IGNvbmRpdGlvbgogIGNvbmRfY2VsbHNbW2NvbmRdXSA9IHNpbXBfYWxsY2VsbHNfY3NzWyxzaW1wX2FsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uPT1jb25kXQogIAogICMgZGVmaW5lIG1ldGFkYXRhCiAgbWV0YV9zaW1wID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyhjb25kX2NlbGxzW1tjb25kXV1AbWV0YS5kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICJDZWxsIiA9IHJvd25hbWVzKGNvbmRfY2VsbHNbW2NvbmRdXUBtZXRhLmRhdGEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJjZWxsX3R5cGUiID0gYXMuY2hhcmFjdGVyKGNvbmRfY2VsbHNbW2NvbmRdXUBtZXRhLmRhdGEkc3VicG9wc19zaW1wKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIHdyaXRlLnRhYmxlKG1ldGFfc2ltcCwgZmlsZSA9IHBhc3RlMChjcGRiX3BhdGgsICIvIiwgY29uZCwgIl9zaW1wLyIsIGNvbmQsICJfbWV0YV9uYW1lcy50eHQiKSwgCiAgICAgICAgICAgIHNlcCA9ICJcdCIsIGNvbC5uYW1lcyA9IFQsIHJvdy5uYW1lcyA9IEYsIHF1b3RlID0gRikKICAgCiAgIyBub3JtYWxpc2UgYW5kIHNhdmUKICBjb25kX2NlbGxzW1tjb25kXV0gPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGNvbmRfY2VsbHNbW2NvbmRdXSwgZG8uY29ycmVjdC51bWkgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJ1bmlxdWVfbmFtZSIsICJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5ydi50aCA9IDEsIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIHZlcmJvc2UgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQogIAogIGRhdCA9IGNiaW5kKHJvd25hbWVzKGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpLAogICAgICAgICAgICAgIE1hdHJpeDo6YXMubWF0cml4KGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpKQogIGNvbG5hbWVzKGRhdClbMV0gPSAiR2VuZSIKICB3cml0ZS50YWJsZShkYXQsIGZpbGUgPSBwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIGNvbmQsICJfc2ltcC8iLCBjb25kLCAiX2V4cF9ub3JtLnR4dCIpLCAKICAgICAgICAgICAgICBzZXAgPSAiXHQiLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCn0KYGBgCgoKIyMgUnVubmluZyBDZWxsUGhvbmVEQgpDb21tYW5kcyBmb3IgcnVubmluIENlbGxQaG9uZURCIHdpbGwgYmUgd3JpdHRlbiBoZXJlLiBSZXN1bHRzIGFyZSBvdXRwdXQgdG8gdGhlIGBsb2NhbDFgIGRpc2sgdG8gYXZvaWQgSS9PIGlzc3VlcywgYnV0IHRoZSBvdXRwdXQgZm9sZGVycyBzaG91bGQgdGhlbiBiZSBjb3BpZWQgdG86IGByZXN1bHRzL2NlbGxfY29tbV9oZWFsdGh5L0NlbGxQaG9uZURCX3J1bnNgLiAgCk5PVEU6IGZvciBzb21lIHJlYXNvbiBJJ20gZ2V0dGluZyBhIFNlZ0ZhdWx0IHdoZW4gdHJ5aW5nIHRvIHJ1biB0aGUgaGVhdG1hcF9wbG90IGNvbW1hbmQuIFRoaXMgd2FzIHJ1biBvbiB0aGUgRXVsZXIgc2VydmVyIGluc3RlYWQuCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoY29uZF9jZWxscykpewogIGNvbW0xID0gcGFzdGUwKCJjZWxscGhvbmVkYiBtZXRob2Qgc3RhdGlzdGljYWxfYW5hbHlzaXMgL2xvY2FsMS9VU0VSUy90b21hc2dvbWVzL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyIsIG4sICJfbWV0YV9uYW1lcy50eHQgL2xvY2FsMS9VU0VSUy90b21hc2dvbWVzL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyIsIG4sICJfZXhwX25vcm0udHh0IC0tdGhyZWFkcz0xMiAtLW91dHB1dC1wYXRoPS9sb2NhbDEvVVNFUlMvdG9tYXNnb21lcy9saXZlci9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgbiwgIiAtLXByb2plY3QtbmFtZSAiLCBuLCAiIC0tY291bnRzLWRhdGEgZ2VuZV9uYW1lIikKICBjb21tMiA9IHBhc3RlMCgiY3AgLXIgL2xvY2FsMS9VU0VSUy90b21hc2dvbWVzL2xpdmVyL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyIsIG4sICIgcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0LyIpCiAgY29tbTMgPSBwYXN0ZTAoImNwIC1yIC9sb2NhbDEvVVNFUlMvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgbiwgIi8iLCBuLCAiX21ldGFfbmFtZXMudHh0IHJlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyIpCiAgY29tbTQgPSBwYXN0ZTAoImNlbGxwaG9uZWRiIHBsb3QgaGVhdG1hcF9wbG90IC0tcHZhbHVlcy1wYXRoIC4vcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICIvcHZhbHVlcy50eHQgLS1vdXRwdXQtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyAtLWNvdW50LW5hbWUgY291bnRzX2hlYXQucGRmIC0tY291bnQtbmV0d29yay1uYW1lIGNvdW50X25ldC50eHQgLS1pbnRlcmFjdGlvbi1jb3VudC1uYW1lIGNvdW50X2ludGVyLnR4dCAvbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICIvIiwgbiwgIl9tZXRhX25hbWVzLnR4dCIpCiAgCiAgcHJpbnQobikKICBwcmludChjb21tMSkKICBwcmludChjb21tMikKICBwcmludChjb21tMykKICBwcmludChjb21tNCkKICBwcmludCgiLiIpCn0KYGBgCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoY29uZF9jZWxscykpewogIGNvbW0xID0gcGFzdGUwKCJjZWxscGhvbmVkYiBtZXRob2Qgc3RhdGlzdGljYWxfYW5hbHlzaXMgL2xvY2FsMS9VU0VSUy90b21hc2dvbWVzL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiX3NpbXAvIiwgbiwgIl9tZXRhX25hbWVzLnR4dCAvbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICJfc2ltcC8iLCBuLCAiX2V4cF9ub3JtLnR4dCAtLXRocmVhZHM9MTIgLS1vdXRwdXQtcGF0aD0vbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvbGl2ZXIvQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICJfc2ltcCAtLXByb2plY3QtbmFtZSAiLCBuLCAiX3NpbXAgLS1jb3VudHMtZGF0YSBnZW5lX25hbWUiKQogIGNvbW0yID0gcGFzdGUwKCJjcCAtciAvbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvbGl2ZXIvQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICJfc2ltcC8iLCBuLCAiX3NpbXAgcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0LyIpCiAgY29tbTMgPSBwYXN0ZTAoImNwIC1yIC9sb2NhbDEvVVNFUlMvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgbiwgIl9zaW1wLyIsIG4sICJfbWV0YV9uYW1lcy50eHQgcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICJfc2ltcC8iKQogIGNvbW00ID0gcGFzdGUwKCJjZWxscGhvbmVkYiBwbG90IGhlYXRtYXBfcGxvdCAtLXB2YWx1ZXMtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiX3NpbXAvcHZhbHVlcy50eHQgLS1vdXRwdXQtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiX3NpbXAvIC0tY291bnQtbmFtZSBjb3VudHNfaGVhdC5wZGYgLS1jb3VudC1uZXR3b3JrLW5hbWUgY291bnRfbmV0LnR4dCAtLWludGVyYWN0aW9uLWNvdW50LW5hbWUgY291bnRfaW50ZXIudHh0IC9sb2NhbDEvVVNFUlMvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgbiwgIl9zaW1wLyIsIG4sICJfbWV0YV9uYW1lcy50eHQiKQogIAogIHByaW50KG4pCiAgcHJpbnQoY29tbTEpCiAgcHJpbnQoY29tbTIpCiAgcHJpbnQoY29tbTMpCiAgcHJpbnQoY29tbTQpCiAgcHJpbnQoIi4iKQp9CmBgYAoKCgojIFByb2Nlc3MgcmVzdWx0cwpMb2FkIHJlc3VsdHMKCmBgYHtyfQpjcGRiX3BhdGggPSAicmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0IgoKc2lnX21lYW5zX25hbWVzX2wgPSBsaXN0KCkKZGVjX2wgPSBsaXN0KCkKbmV0X25hbWVzX2wgPSBsaXN0KCkKbWV0YV9sID0gbGlzdCgpCmNvbmRzID0gdW5pcXVlKGFsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uKQpmb3IobiBpbiBjKGNvbmRzLCBwYXN0ZTAoY29uZHMsICJfc2ltcCIpKSl7CiAgbnMgPSBzdHJzcGxpdChuLCAiXyIpW1sxXV1bMV0KICBzaWdfbWVhbnNfbmFtZXNfbFtbbl1dID0gcmVhZC50YWJsZShwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIG4sICIvc2lnbmlmaWNhbnRfbWVhbnMudHh0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgc2VwID0gIlx0Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgZGVjX2xbW25dXSA9IHJlYWQudGFibGUocGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBuLCAiL2RlY29udm9sdXRlZC50eHQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCBzZXAgPSAiXHQiKQogIGNvbG5hbWVzKGRlY19sW1tuXV0pID0gZ3N1YigiLiIsICIgIiwgY29sbmFtZXMoZGVjX2xbW25dXSksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09ImdkIFQgY2VsbHMiXSA9ICJnZC1UIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTWFjcm9waGFnZXMgIEhFUzQgICJdID0gIk1hY3JvcGhhZ2VzIChIRVM0KykiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJDRDggYWIgVCBjZWxscyJdID0gIkNEOCBhYi1UIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iQ0Q4IGFiIFQgY2VsbHMgMSJdID0gIkNEOCBhYi1UIGNlbGxzIDEiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJDRDggYWIgVCBjZWxscyAyIl0gPSAiQ0Q4IGFiLVQgY2VsbHMgMiIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkNEOCBhYiBUIGNlbGxzIDMiXSA9ICJDRDggYWItVCBjZWxscyAzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTmFpdmUgQ0Q0ICBUIGNlbGxzIl0gPSAiTmFpdmUgQ0Q0KyBUIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iYWIgVCBjZWxscyAgc3RyZXNzICJdID0gImFiLVQgY2VsbHMgKHN0cmVzcykiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJNb25vY3l0ZXMgIHNlY3JldG9yeSAiXSA9ICJNb25vY3l0ZXMgKHNlY3JldG9yeSkiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJNb25vY3l0ZXMgIFRSRU0yICBDRDkgICJdID0gIk1vbm9jeXRlcyAoVFJFTTIrIENEOSspIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTW9ub2N5dGVzICBJR1NGMjEgIEdQUjM0ICAiXSA9ICJNb25vY3l0ZXMgKElHU0YyMSsgR1BSMzQrKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkxTRUMgIHN0cmVzcyAiXSA9ICJMU0VDIChzdHJlc3MpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgcmVtb2RlbGxpbmcgIl0gPSAiTFNFQyAocmVtb2RlbGxpbmcpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgaW50ZXJmZXJvbiAiXSA9ICJMU0VDIChpbnRlcmZlcm9uKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkxTRUMgIGhpZ2ggTVQgMiAiXSA9ICJMU0VDIChoaWdoIE1UIDIpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgaGlnaCBNVCAxICJdID0gIkxTRUMgKGhpZ2ggTVQgMSkiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJMU0VDICBoaWdoIE1UICJdID0gIkxTRUMgKGhpZ2ggTVQpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgZmVuZXN0ciAgIl0gPSAiTFNFQyAoZmVuZXN0ci4pIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iS3VwZmZlciBjZWxscyAgU1VDTlIxICAiXSA9ICJLdXBmZmVyIGNlbGxzIChTVUNOUjErKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IklnRyAgUGxhc21hIGNlbGxzIl0gPSAiSWdHKyBQbGFzbWEgY2VsbHMiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJJZ0EgIFBsYXNtYSBjZWxscyJdID0gIklnQSsgUGxhc21hIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iRUMgbm9uIExTRUMiXSA9ICJFQyBub24tTFNFQyIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkRpdmlkaW5nIFQgTksgY2VsbHMiXSA9ICJEaXZpZGluZyBUL05LIGNlbGxzIgogIG5ldF9uYW1lc19sW1tuXV0gPSByZWFkLnRhYmxlKHBhc3RlMChjcGRiX3BhdGgsICIvIiwgbiwgIi9jb3VudF9uZXQudHh0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgc2VwID0gIlx0Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgbWV0YV9sW1tuXV0gPSBpZighZ3JlcGwoInNpbXAiLCBuKSl7CiAgICByZWFkLnRhYmxlKHBhc3RlMChjcGRiX3BhdGgsICIvIiwgbiwgIi8iLCBuLCAiX21ldGFfbmFtZXMudHh0IiksaGVhZGVyID0gVCxzZXAgPSAiXHQiKQogIH0gZWxzZXsKICAgIHJlYWQudGFibGUocGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBuLCAiLyIsIG5zLCAiX21ldGFfbmFtZXMudHh0IiksaGVhZGVyID0gVCxzZXAgPSAiXHQiKQogIH0KfQpzYXZlUkRTKGRlY19sLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvZGVjb252b2x1dGVkX2xpc3QuUkRTIikKc2F2ZVJEUyhuZXRfbmFtZXNfbCwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L2NvdW50X25ldF9saXN0LlJEUyIpCmBgYAoKUmVmb3JtYXQgc2lnbmlmaWNhbnQgbWVhbnMgbWF0cml4CgpgYGB7cn0KcmVmb3JtX2xpc3QgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMobWV0YV9sKSl7CiAgc2ltcF9yZWZvcm0gPSByZXNoYXBlMjo6bWVsdChzaWdfbWVhbnNfbmFtZXNfbFtbbl1dWyxjKDE6NCw3OjksMTM6bmNvbChzaWdfbWVhbnNfbmFtZXNfbFtbbl1dKSldKQogIHNpbXBfcmVmb3JtID0gc2ltcF9yZWZvcm1bY29tcGxldGUuY2FzZXMoc2ltcF9yZWZvcm0pLF0KICAKICBscjEgPSBjKCkKICBscjIgPSBjKCkKICBmb3IoaSBpbiAxOm5yb3coc2ltcF9yZWZvcm0pKXsKICAgIGlmKGdyZXBsKCJjb21wbGV4Iiwgc2ltcF9yZWZvcm0kcGFydG5lcl9hW2ldKSl7CiAgICAgIGxyMSA9IGMobHIxLCBzdWJzdHIoc2ltcF9yZWZvcm0kcGFydG5lcl9hW2ldLCA5LDEwMCkpCiAgICB9IGVsc2V7CiAgICAgIGxyMSA9IGMobHIxLCBzdHJzcGxpdChzaW1wX3JlZm9ybSRpbnRlcmFjdGluZ19wYWlyW2ldLCAiXyIpW1sxXV1bMV0pCiAgICB9CiAgICBpZihncmVwbCgiY29tcGxleCIsIHNpbXBfcmVmb3JtJHBhcnRuZXJfYltpXSkpewogICAgICBscjIgPSBjKGxyMiwgc3Vic3RyKHNpbXBfcmVmb3JtJHBhcnRuZXJfYltpXSwgOSwxMDApKQogICAgfSBlbHNlewogICAgICBzcGx0bGVuID0gbGVuZ3RoKHN0cnNwbGl0KHNpbXBfcmVmb3JtJGludGVyYWN0aW5nX3BhaXJbaV0sICJfIilbWzFdXSkKICAgICAgbHIyID0gYyhscjIsIHN0cnNwbGl0KHNpbXBfcmVmb3JtJGludGVyYWN0aW5nX3BhaXJbaV0sICJfIilbWzFdXVtzcGx0bGVuXSkKICAgIH0KICB9CiAgc2ltcF9yZWZvcm0kbHIxID0gbHIxCiAgc2ltcF9yZWZvcm0kbHIyID0gbHIyCiAgCiAgc2ltcF9yZWZvcm0kY3QxID0gTkEKICBzaW1wX3JlZm9ybSRjdDIgPSBOQQogIGRvbmVzdCA9IHJlcChOQSwgbGVuZ3RoKHNpbXBfcmVmb3JtJGN0MSkpCiAgZG9uZWVuID0gcmVwKE5BLCBsZW5ndGgoc2ltcF9yZWZvcm0kY3QyKSkKICBmb3IoY3QgaW4gc29ydCh1bmlxdWUobWV0YV9sW1tuXV0kY2VsbF90eXBlKSkpewogICAgY3RfbW9kID0gZ3N1YigiLSIsICIgIiwgY3QsIGZpeGVkID0gVCkKICAgIGN0X21vZCA9IGdzdWIoIisiLCAiICIsIGN0X21vZCwgZml4ZWQgPSBUKQogICAgY3RfbW9kID0gZ3N1YigiLyIsICIgIiwgY3RfbW9kLCBmaXhlZCA9IFQpCiAgICBjdF9tb2QgPSBnc3ViKCIoIiwgIiAiLCBjdF9tb2QsIGZpeGVkID0gVCkKICAgIGN0X21vZCA9IGdzdWIoIikiLCAiICIsIGN0X21vZCwgZml4ZWQgPSBUKQogICAgY3RfbW9kID0gZ3N1YigiLiIsICIgIiwgY3RfbW9kLCBmaXhlZCA9IFQpCiAgICBzdCA9IGdyZXBsKHBhc3RlMCgiXiIsY3RfbW9kLCAiICIpLCBnc3ViKCIuIiwgIiAiLCBzaW1wX3JlZm9ybSR2YXJpYWJsZSwgZml4ZWQgPSBUKSwgZml4ZWQgPSBGKQogICAgZW4gPSBncmVwbChwYXN0ZTAoIiAiLGN0X21vZCwiJCIpLCBnc3ViKCIuIiwgIiAiLCBzaW1wX3JlZm9ybSR2YXJpYWJsZSwgZml4ZWQgPSBUKSwgZml4ZWQgPSBGKQogICAgc2ltcF9yZWZvcm0kY3QxW3N0ICYgaXMubmEoZG9uZXN0KV0gPSBjdAogICAgc2ltcF9yZWZvcm0kY3QyW2VuICYgaXMubmEoZG9uZWVuKV0gPSBjdAogICAgZG9uZXN0W3N0XSA9IFQKICAgIGRvbmVlbltlbl0gPSBUCiAgfQogICNjb2xuYW1lcyhkZWNfbFtbbl1dKVshKGNvbG5hbWVzKGRlY19sW1tuXV0pICVpbiUgdW5pcXVlKHNpbXBfcmVmb3JtJGN0MikpXQogIAogICMgZGlyZWN0aW9uOiByZWNlcHRvcnMgYWN0aXZhdGUgaW50ZXJuYWwgc2lnbmFsIC0gdGhhdCBpcyB0aGUgZGlyZWN0aW9uIG9mIHRoZSBzaWduYWxsaW5nCiAgIyMgaWYgYm90aCBhcmUgdHJ1ZSwgdGhlICJuZXQgc2lnbmFsIiBpcyAwCiAgc2ltcF9yZWZvcm0kZGlyID0gaWZlbHNlKHNpbXBfcmVmb3JtJHJlY2VwdG9yX2E9PXNpbXBfcmVmb3JtJHJlY2VwdG9yX2IsIDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc2ltcF9yZWZvcm0kcmVjZXB0b3JfYT09IlRydWUiICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpbXBfcmVmb3JtJHJlY2VwdG9yX2I9PSJGYWxzZSIsIC0xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNpbXBfcmVmb3JtJHJlY2VwdG9yX2E9PSJGYWxzZSIgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2ltcF9yZWZvcm0kcmVjZXB0b3JfYj09IlRydWUiLCAxLCBOQSkpKQogIAogIHNpbXBfcmVmb3JtID0gc2ltcF9yZWZvcm1bLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgImN0MSIsICJjdDIiLCAibHIxIiwgImxyMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiwgImRpciIpXQogIAogIGZvcihpIGluIDE6bnJvdyhzaW1wX3JlZm9ybSkpewogICAgaWYoc2ltcF9yZWZvcm1baSwiZGlyIl09PS0xKXsKICAgICAgdG1wID0gc2ltcF9yZWZvcm1baSwiY3QyIl0KICAgICAgc2ltcF9yZWZvcm1baSwiY3QyIl0gPSBzaW1wX3JlZm9ybVtpLCJjdDEiXQogICAgICBzaW1wX3JlZm9ybVtpLCJjdDEiXSA9IHRtcAogICAgICAKICAgICAgdG1wID0gc2ltcF9yZWZvcm1baSwibHIyIl0KICAgICAgc2ltcF9yZWZvcm1baSwibHIyIl0gPSBzaW1wX3JlZm9ybVtpLCJscjEiXQogICAgICBzaW1wX3JlZm9ybVtpLCJscjEiXSA9IHRtcAogICAgICAKICAgICAgc2ltcF9yZWZvcm1baSwiZGlyIl09MQogICAgfQogIH0KICAKIHJlZm9ybV9saXN0W1tuXV0gPSBzaW1wX3JlZm9ybQp9CnNhdmVSRFMocmVmb3JtX2xpc3QsIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9yZWZvcm1fbGlzdC5SRFMiKQpgYGAKClBsb3QgaW50ZXJhY3Rpb24gY291bnRzCgpgYGB7cn0KaW50ZXJfY291bnRzX2wgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMobmV0X25hbWVzX2wpKXsKICBpbnRlcl9jb3VudHMgPSByZXNoYXBlMjo6ZGNhc3QoZGF0YSA9IG5ldF9uYW1lc19sW1tuXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtdWxhID0gU09VUkNFflRBUkdFVCwgdmFsdWUudmFyID0gImNvdW50IikKICByb3duYW1lcyhpbnRlcl9jb3VudHMpID0gaW50ZXJfY291bnRzWywxXQogIGludGVyX2NvdW50cyA9IGludGVyX2NvdW50c1ssLTFdCiAgCiAgcGRmKHBhc3RlMCgicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC8iLCBuLCAiX251bWJlcl9vZl9pbnRlcmFjdGlvbnMucGRmIiksIHVzZURpbmdiYXRzID0gRiwgCiAgICAgIGhlaWdodCA9IDEwLCB3aWR0aCA9IDEwKQogIHBoZWF0bWFwOjpwaGVhdG1hcChpbnRlcl9jb3VudHMsIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiLCBtYWluID0gIkFsbCBjbHVzdGVycyIpCiAgZGV2Lm9mZigpCiAgaW50ZXJfY291bnRzX2xbW25dXSA9IGludGVyX2NvdW50cwp9CmBgYAoKTWFraW5nIHR3byBjZWxsIHR5cGUgYnkgaW50ZXJhY3Rpb24gbWF0cmljZXM6IG9uZSBoYXMgdGhlIGxpZ2FuZCBtZWFuLCBhbm90aGVyIHRoZSByZWNlcHRvciBtZWFuCgpgYGB7cn0Kc2NvcmluZ19tYXRzID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKHJlZm9ybV9saXN0KSl7CiAgY2xfcmVmb3JtID0gcmVmb3JtX2xpc3RbW25dXQogICMgYWRkIHJldmVyc2VkIG5vbi1kaXJlY3RlZCBpbnRlcmFjdGlvbnMsIHRvIGNvbnNpZGVyIHRoZW0gaW4gYm90aCBkaXJlY3Rpb25zCiAgY2xfcmVmb3JtMCA9IGNsX3JlZm9ybVtjbF9yZWZvcm0kZGlyPT0wLF0KICBjbF9yZWZvcm0wID0gY2xfcmVmb3JtMFssYygxLDMsMiw1LDQsNiw3KV0KICBjb2xuYW1lcyhjbF9yZWZvcm0wKSA9IGNvbG5hbWVzKGNsX3JlZm9ybSkKICBjbF9yZWZvcm0gPSByYmluZChjbF9yZWZvcm0sIGNsX3JlZm9ybTApCiAgCiAgZGVjX2NsID0gZGVjX2xbW25dXQogIAogIG1hdF9saWdfY2wgPSBkYXRhLmZyYW1lKG1hdHJpeChOQSwgbnJvdyA9IGxlbmd0aCh1bmlxdWUoY2xfcmVmb3JtJGlkX2NwX2ludGVyYWN0aW9uKSksIAogICAgICAgICAgICAgICAgICAgbmNvbCA9IGxlbmd0aCh1bmlxdWUoY2xfcmVmb3JtJGN0MSkpKSkKICBtYXRfcmVjX2NsID0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5yb3cgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRpZF9jcF9pbnRlcmFjdGlvbikpLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRjdDEpKSkpCiAgY29sbmFtZXMobWF0X2xpZ19jbCkgPSBjb2xuYW1lcyhtYXRfcmVjX2NsKSA9IHVuaXF1ZShjbF9yZWZvcm0kY3QxKQogIHJvd25hbWVzKG1hdF9saWdfY2wpID0gcm93bmFtZXMobWF0X3JlY19jbCkgPSB1bmlxdWUoY2xfcmVmb3JtJGlkX2NwX2ludGVyYWN0aW9uKQogIGZvcihpIGluIDE6bnJvdyhjbF9yZWZvcm0pKXsKICAgIGcxID0gY2xfcmVmb3JtW2ksImxyMSJdCiAgICBnMiA9IGNsX3JlZm9ybVtpLCJscjIiXQogICAgCiAgICBjMSA9IGNsX3JlZm9ybVtpLCJjdDEiXQogICAgYzIgPSBjbF9yZWZvcm1baSwiY3QyIl0KICAgIAogICAgaW50ID0gYXMuY2hhcmFjdGVyKGNsX3JlZm9ybVtpLDFdKQogICAgCiAgICBtMSA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWywxXT09ZzEsYzFdLCBuYS5ybSA9IFQpCiAgICBpZihpcy5uYShtMSkpIG0xID0gbWVhbihkZWNfY2xbZGVjX2NsJGlkX2NwX2ludGVyYWN0aW9uPT1pbnQgJiBkZWNfY2xbLDVdPT1nMSxjMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUKQogICAgbWF0X2xpZ19jbFtpbnQsIGMxXSA9IG0xCiAgICAKICAgIG0yID0gbWVhbihkZWNfY2xbZGVjX2NsJGlkX2NwX2ludGVyYWN0aW9uPT1pbnQgJiBkZWNfY2xbLDFdPT1nMixjMl0sIG5hLnJtID0gVCkKICAgIGlmKGlzLm5hKG0yKSkgbTIgPSBtZWFuKGRlY19jbFtkZWNfY2wkaWRfY3BfaW50ZXJhY3Rpb249PWludCAmIGRlY19jbFssNV09PWcyLGMyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVCkKICAgIGlmKGlzLm5hKG0xKSB8IGlzLm5hKG0yKSkgcHJpbnQoaW50KQogICAgbWF0X3JlY19jbFtpbnQsIGMyXSA9IG0yCiAgfQogIAogICMgbGlnYW5kIHZzIHJlY2VwdG9yIGNvcnJlbGF0aW9uCiAgY29yX21hdF9lYWNoX2NsID0gY29yKG1hdF9saWdfY2wsIG1hdF9yZWNfY2wsIHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInNwIikKICAKICBzY29yaW5nX21hdHNbW25dXSA9IGxpc3QoIm1hdF9saWdfY3QiID0gbWF0X2xpZ19jbCwgIm1hdF9yZWNfY3QiID0gbWF0X3JlY19jbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvcl9tYXRfZWFjaCIgPSBjb3JfbWF0X2VhY2hfY2wpCiAgCiAgIyBzdW0obGlnLCByZWcpIGNvcnJlbGF0aW9uCiAgbWF0X2xpZ19jbFtpcy5uYShtYXRfbGlnX2NsKV0gPSAwCiAgbWF0X3JlY19jbFtpcy5uYShtYXRfcmVjX2NsKV0gPSAwCiAgbWF0X2JvdGhfY2wgPSBtYXRfbGlnX2NsK21hdF9yZWNfY2wKICBtYXRfYm90aF9jbFttYXRfYm90aF9jbD09MF0gPSBOQQogIGNvcl9tYXRfYm90aF9jbCA9IGNvcihtYXRfYm90aF9jbCwgdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAic3AiKQogIAogIHNjb3JpbmdfbWF0c1tbbl1dJGNvcl9tYXRfYm90aCA9IGNvcl9tYXRfYm90aF9jbAp9CnNhdmVSRFMoc2NvcmluZ19tYXRzLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvc2NvcmluZ19tYXRzLlJEUyIpCmBgYAoKTWFraW5nIHR3byBjZWxsIHR5cGUgYnkgaW50ZXJhY3Rpb24gbWF0cmljZXM6IG9uZSBoYXMgdGhlIGxpZ2FuZCBtZWFuLCBhbm90aGVyIHRoZSByZWNlcHRvciBtZWFuIChoZXJlIG9ubHkgZGlyZWN0aW9uYWwpCgpgYGB7cn0Kc2NvcmluZ19tYXRzX2RpciA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhyZWZvcm1fbGlzdCkpewogIGNsX3JlZm9ybSA9IHJlZm9ybV9saXN0W1tuXV0KICAjIGFkZCByZXZlcnNlZCBub24tZGlyZWN0ZWQgaW50ZXJhY3Rpb25zLCB0byBjb25zaWRlciB0aGVtIGluIGJvdGggZGlyZWN0aW9ucwogIGNsX3JlZm9ybTAgPSBjbF9yZWZvcm1bY2xfcmVmb3JtJGRpcj09MCxdCiAgY2xfcmVmb3JtMCA9IGNsX3JlZm9ybTBbLGMoMSwzLDIsNSw0LDYsNyldCiAgY29sbmFtZXMoY2xfcmVmb3JtMCkgPSBjb2xuYW1lcyhjbF9yZWZvcm0pCiAgY2xfcmVmb3JtID0gcmJpbmQoY2xfcmVmb3JtLCBjbF9yZWZvcm0wKQogIAogIGNsX3JlZm9ybSA9IGNsX3JlZm9ybVtjbF9yZWZvcm0kZGlyPT0xLF0KICAKICBkZWNfY2wgPSBkZWNfbFtbbl1dCiAgCiAgbWF0X2xpZ19jbCA9IGRhdGEuZnJhbWUobWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kaWRfY3BfaW50ZXJhY3Rpb24pKSwgCiAgICAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kY3QxKSkpKQogIG1hdF9yZWNfY2wgPSBkYXRhLmZyYW1lKG1hdHJpeChOQSwgbnJvdyA9IGxlbmd0aCh1bmlxdWUoY2xfcmVmb3JtJGlkX2NwX2ludGVyYWN0aW9uKSksIAogICAgICAgICAgICAgICAgICAgbmNvbCA9IGxlbmd0aCh1bmlxdWUoY2xfcmVmb3JtJGN0MSkpKSkKICBjb2xuYW1lcyhtYXRfbGlnX2NsKSA9IGNvbG5hbWVzKG1hdF9yZWNfY2wpID0gdW5pcXVlKGNsX3JlZm9ybSRjdDEpCiAgcm93bmFtZXMobWF0X2xpZ19jbCkgPSByb3duYW1lcyhtYXRfcmVjX2NsKSA9IHVuaXF1ZShjbF9yZWZvcm0kaWRfY3BfaW50ZXJhY3Rpb24pCiAgZm9yKGkgaW4gMTpucm93KGNsX3JlZm9ybSkpewogICAgZzEgPSBjbF9yZWZvcm1baSwibHIxIl0KICAgIGcyID0gY2xfcmVmb3JtW2ksImxyMiJdCiAgICAKICAgIGMxID0gY2xfcmVmb3JtW2ksImN0MSJdCiAgICBjMiA9IGNsX3JlZm9ybVtpLCJjdDIiXQogICAgCiAgICBpbnQgPSBhcy5jaGFyYWN0ZXIoY2xfcmVmb3JtW2ksMV0pCiAgICAKICAgIG0xID0gbWVhbihkZWNfY2xbZGVjX2NsJGlkX2NwX2ludGVyYWN0aW9uPT1pbnQgJiBkZWNfY2xbLDFdPT1nMSxjMV0sIG5hLnJtID0gVCkKICAgIGlmKGlzLm5hKG0xKSkgbTEgPSBtZWFuKGRlY19jbFtkZWNfY2wkaWRfY3BfaW50ZXJhY3Rpb249PWludCAmIGRlY19jbFssNV09PWcxLGMxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFQpCiAgICBtYXRfbGlnX2NsW2ludCwgYzFdID0gbTEKICAgIAogICAgbTIgPSBtZWFuKGRlY19jbFtkZWNfY2wkaWRfY3BfaW50ZXJhY3Rpb249PWludCAmIGRlY19jbFssMV09PWcyLGMyXSwgbmEucm0gPSBUKQogICAgaWYoaXMubmEobTIpKSBtMiA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWyw1XT09ZzIsYzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUKQogICAgaWYoaXMubmEobTEpIHwgaXMubmEobTIpKSBwcmludChpbnQpCiAgICBtYXRfcmVjX2NsW2ludCwgYzJdID0gbTIKICB9CiAgCiAgIyBsaWdhbmQgdnMgcmVjZXB0b3IgY29ycmVsYXRpb24KICBjb3JfbWF0X2VhY2hfY2wgPSBjb3IobWF0X2xpZ19jbCwgbWF0X3JlY19jbCwgdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAic3AiKQogIAogIHNjb3JpbmdfbWF0c19kaXJbW25dXSA9IGxpc3QoIm1hdF9saWdfY3QiID0gbWF0X2xpZ19jbCwgIm1hdF9yZWNfY3QiID0gbWF0X3JlY19jbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb3JfbWF0X2VhY2giID0gY29yX21hdF9lYWNoX2NsKQogIAogICMgc3VtKGxpZywgcmVnKSBjb3JyZWxhdGlvbgogIG1hdF9saWdfY2xbaXMubmEobWF0X2xpZ19jbCldID0gMAogIG1hdF9yZWNfY2xbaXMubmEobWF0X3JlY19jbCldID0gMAogIG1hdF9ib3RoX2NsID0gbWF0X2xpZ19jbCttYXRfcmVjX2NsCiAgbWF0X2JvdGhfY2xbbWF0X2JvdGhfY2w9PTBdID0gTkEKICBjb3JfbWF0X2JvdGhfY2wgPSBjb3IobWF0X2JvdGhfY2wsIHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInNwIikKICAKICBzY29yaW5nX21hdHNfZGlyW1tuXV0kY29yX21hdF9ib3RoID0gY29yX21hdF9ib3RoX2NsCn0Kc2F2ZVJEUyhzY29yaW5nX21hdHNfZGlyLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvc2NvcmluZ19tYXRzX2Rpci5SRFMiKQpgYGAKCkNvdW50IGludGVyYWN0aW9ucyBvZiBlYWNoIHR5cGUKCmBgYHtyfQppbnRlcl9jb3VudHNfZGlyID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKHJlZm9ybV9saXN0KSl7CiAgaW50ZXJfY291bnRzX2Rpcltbbl1dID0gbGlzdCgpCiAgZm9yKGkgaW4gYygwLDEpKXsKICAgIGNsX3JlZm9ybSA9IHJlZm9ybV9saXN0W1tuXV0KICAgIGNsX3JlZm9ybSA9IGNsX3JlZm9ybVtjbF9yZWZvcm0kZGlyPT1pLF0KICAgIAogICAgbl9kaXJfaW50ID0gbWF0cml4KDAsIG5yb3cgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRjdDEpKSwKICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kY3QxKSkpCiAgICByb3duYW1lcyhuX2Rpcl9pbnQpID0gY29sbmFtZXMobl9kaXJfaW50KSA9IHVuaXF1ZShjbF9yZWZvcm0kY3QxKQogICAgZm9yKGMxIGluIHVuaXF1ZShjbF9yZWZvcm0kY3QxKSl7CiAgICAgIGZvcihjMiBpbiB1bmlxdWUoY2xfcmVmb3JtJGN0MSkpewogICAgICAgIG5fZGlyX2ludFtjMSxjMl0gPSBucm93KGNsX3JlZm9ybVtjbF9yZWZvcm0kY3QxPT1jMSAmIGNsX3JlZm9ybSRjdDI9PWMyLF0pCiAgICAgICAgaWYoaT09MCl7CiAgICAgICAgICBuX2Rpcl9pbnRbYzEsYzJdID0gbl9kaXJfaW50W2MxLGMyXStucm93KGNsX3JlZm9ybVtjbF9yZWZvcm0kY3QxPT1jMiAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsX3JlZm9ybSRjdDI9PWMxLF0pCiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICBpbnRlcl9jb3VudHNfZGlyW1tuXV1bW2FzLmNoYXJhY3RlcihpKV1dID0gbl9kaXJfaW50CiAgfQp9CnNhdmVSRFMoaW50ZXJfY291bnRzX2RpciwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L2ludGVyX2NvdW50c19kaXIuUkRTIikKYGBgCgoKCiMgSW50ZXJhY3Rpb24gYW5hbHlzaXMgaW4gY29uZGl0aW9ucwpDb21wYXJlIGludGVyYWN0aW9ucyB3aXRoIERFIGdlbmVzCgpgYGB7cn0KIyBhZGQgZ2VuZXMgZnJvbSBjb21wbGV4ZXMKY29tcGxleF9yZWYgPSB1bmlxdWUocmJpbmQoZGVjX2wkaGVhbHRoeVtkZWNfbCRoZWFsdGh5JGlzX2NvbXBsZXg9PSJUcnVlIixjKDEsNSldLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjX2wkZW1ib2xpc2VkW2RlY19sJGVtYm9saXNlZCRpc19jb21wbGV4PT0iVHJ1ZSIsYygxLDUpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjX2wkcmVnZW5lcmF0aW5nW2RlY19sJHJlZ2VuZXJhdGluZyRpc19jb21wbGV4PT0iVHJ1ZSIsYygxLDUpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjX2wkaGVhbHRoeV9zaW1wW2RlY19sJGhlYWx0aHlfc2ltcCRpc19jb21wbGV4PT0iVHJ1ZSIsYygxLDUpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjX2wkZW1ib2xpc2VkX3NpbXBbZGVjX2wkZW1ib2xpc2VkX3NpbXAkaXNfY29tcGxleD09IlRydWUiLGMoMSw1KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJHJlZ2VuZXJhdGluZ19zaW1wW2RlY19sJHJlZ2VuZXJhdGluZ19zaW1wJGlzX2NvbXBsZXg9PSJUcnVlIixjKDEsNSldKSkKaW50ZXJfZGYgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMocmVmb3JtX2xpc3QpKXsKICB0bXAgPSBtZXJnZShyZWZvcm1fbGlzdFtbbl1dLCBjb21wbGV4X3JlZiwgYnkueCA9IDQsIGJ5LnkgPSAyLCBhbGwueCA9IFQpWyxjKDIsMyw0LDEsNSw4LDYsNyldCiAgaW50ZXJfZGZbW25dXSA9IG1lcmdlKHRtcCwgY29tcGxleF9yZWYsIGJ5LnggPSA1LCBieS55ID0gMiwgYWxsLnggPSBUKVssYygyLDMsNCw1LDYsMSw5LDcsOCldCiAgaW50ZXJfZGZbW25dXSRnZW5lX25hbWUueFtpcy5uYShpbnRlcl9kZltbbl1dJGdlbmVfbmFtZS54KV0gPSBpbnRlcl9kZltbbl1dJGxyMVtpcy5uYShpbnRlcl9kZltbbl1dJGdlbmVfbmFtZS54KV0KICBpbnRlcl9kZltbbl1dJGdlbmVfbmFtZS55W2lzLm5hKGludGVyX2RmW1tuXV0kZ2VuZV9uYW1lLnkpXSA9IGludGVyX2RmW1tuXV0kbHIyW2lzLm5hKGludGVyX2RmW1tuXV0kZ2VuZV9uYW1lLnkpXQogIGNvbG5hbWVzKGludGVyX2RmW1tuXV0pW2MoNSw3KV0gPSBjKCJnbjEiLCAiZ24yIikKfQoKZm9yKGNjIGluIG5hbWVzKGludGVyX2RmKSl7CiAgIyBpbnRlcmFjdGlvbiB1bmlxdWUgaW4gY29uZGl0aW9uCiAgdW5pcXVlX2ludGVyID0gc2V0ZGlmZih1bmlxdWUoaW50ZXJfZGZbW2NjXV0kaWRfY3BfaW50ZXJhY3Rpb24pLAogICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGMoaW50ZXJfZGZbW25hbWVzKGludGVyX2RmKVtuYW1lcyhpbnRlcl9kZikhPWNjXVsxXV1dJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGZbW25hbWVzKGludGVyX2RmKVtuYW1lcyhpbnRlcl9kZikhPWNjXVsyXV1dJGlkX2NwX2ludGVyYWN0aW9uKSkpCiAgaW50ZXJfZGZbW2NjXV0kY3BkYl91bmlxdWUgPSBpbnRlcl9kZltbY2NdXSRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIHVuaXF1ZV9pbnRlcgogIAogICMgbHIxIHVuaXF1ZSBpbiBjb25kaXRpb24KICB1bmlxdWVfbHIxID0gc2V0ZGlmZih1bmlxdWUoaW50ZXJfZGZbW2NjXV0kbHIxKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjKGludGVyX2RmW1tuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMV1dXSRscjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZltbbmFtZXMoaW50ZXJfZGYpW25hbWVzKGludGVyX2RmKSE9Y2NdWzJdXV0kbHIxKSkpCiAgaW50ZXJfZGZbW2NjXV0kY3BkYl91bmlxdWVfbHIxID0gaW50ZXJfZGZbW2NjXV0kbHIxICVpbiUgdW5pcXVlX2xyMQogIAogICMgbHIyIHVuaXF1ZSBpbiBjb25kaXRpb24KICB1bmlxdWVfbHIyID0gc2V0ZGlmZih1bmlxdWUoaW50ZXJfZGZbW2NjXV0kbHIyKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjKGludGVyX2RmW1tuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMV1dXSRscjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZltbbmFtZXMoaW50ZXJfZGYpW25hbWVzKGludGVyX2RmKSE9Y2NdWzJdXV0kbHIyKSkpCiAgaW50ZXJfZGZbW2NjXV0kY3BkYl91bmlxdWVfbHIyID0gaW50ZXJfZGZbW2NjXV0kbHIyICVpbiUgdW5pcXVlX2xyMgp9CmZvcihjYyBpbiBuYW1lcyhpbnRlcl9kZikpewogIG9jMSA9IG5hbWVzKGludGVyX2RmKVtuYW1lcyhpbnRlcl9kZikhPWNjXVsxXQogIG9jMiA9IG5hbWVzKGludGVyX2RmKVtuYW1lcyhpbnRlcl9kZikhPWNjXVsyXQogIGludDEgPSBwYXN0ZTAoaW50ZXJfZGZbW2NjXV0kaWRfY3BfaW50ZXJhY3Rpb24saW50ZXJfZGZbW2NjXV0kY3QxLGludGVyX2RmW1tjY11dJGN0MikKICBpbnQyID0gdW5pcXVlKHBhc3RlMChpbnRlcl9kZltbb2MxXV0kaWRfY3BfaW50ZXJhY3Rpb24saW50ZXJfZGZbW29jMV1dJGN0MSxpbnRlcl9kZltbb2MxXV0kY3QyKSkKICBpbnQzID0gdW5pcXVlKHBhc3RlMChpbnRlcl9kZltbb2MyXV0kaWRfY3BfaW50ZXJhY3Rpb24saW50ZXJfZGZbW29jMl1dJGN0MSxpbnRlcl9kZltbb2MyXV0kY3QyKSkKICB1bmlxdWVfaW50ZXIgPSBzZXRkaWZmKHVuaXF1ZShpbnQxKSwgdW5pcXVlKGMoaW50MiwgaW50MykpKQogIGludGVyX2RmW1tjY11dJGNwZGJfdW5pcXVlX2N0ID0gaW50MSAlaW4lIHVuaXF1ZV9pbnRlcgp9CmZvcihuIGluIG5hbWVzKGludGVyX2RmKSl7CiAgZGYgPSBpbnRlcl9kZltbbl1dCiAgZGYgPSB1bmlxdWUoZGZbLGMoMTozKV0pCiAgCiAgaW50c191bl9jdCA9IHJvd1N1bXModGFibGUoZGYkaWRfY3BfaW50ZXJhY3Rpb24sIGRmJGN0MSk+MCk8NCB8IAogICAgcm93U3Vtcyh0YWJsZShkZiRpZF9jcF9pbnRlcmFjdGlvbiwgZGYkY3QyKT4wKTw0CiAgCiAgaW50ZXJfZGZbW25dXSRpbnRlcl9jdF9zcGVjID0gaW50ZXJfZGZbW25dXSRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIG5hbWVzKGludHNfdW5fY3QpW2ludHNfdW5fY3RdCn0KZm9yKG4gaW4gbmFtZXMoaW50ZXJfZGYpKXsKICB4eHggPSBkYXRhLmZyYW1lKGN0ID0gYyhpbnRlcl9kZltbbl1dWywyXSwgaW50ZXJfZGZbW25dXVssM10pLCAKICAgICAgICAgICAgICAgICAgIGxyID0gYyhpbnRlcl9kZltbbl1dWyw0XSwgaW50ZXJfZGZbW25dXVssNl0pKQogIHh4eCA9IHVuaXF1ZSh4eHgpCiAgdGFiX2xyID0gKHRhYmxlKHh4eCRsciwgeHh4JGN0KT4wKSoxCiAgdGFiX2xyID0gcm93U3Vtcyh0YWJfbHIpCiAgCiAgaW50ZXJfZGZbW25dXSRscjFfbmN0ID0gdGFiX2xyW2ludGVyX2RmW1tuXV0kbHIxXQogIGludGVyX2RmW1tuXV0kbHIyX25jdCA9IHRhYl9scltpbnRlcl9kZltbbl1dJGxyMl0KfQpzYXZlUkRTKGludGVyX2RmLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvY29uZF9kaWZmX2ludGVyYWN0X0RFLlJEUyIpCmBgYAoKUGxvdCBpbnRlcmFjdGlvbiBoZWF0bWFwIC0gdG90YWwgYW5kIGZpbHRlcmVkCgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQppbnRfY291bnRzX3RvdGFsID0gbGlzdCgpCmludF9jb3VudHNfZmlsdCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhpbnRlcl9kZikpewogIHN1YmRmY3QgPSB1bmlxdWUoaW50ZXJfZGZbW25dXVssMTozXSkKICBzdWJkZmN0ID0gdW5pcXVlKHN1YmRmY3QpCiAgZGZjdCA9IGRhdGEuZnJhbWUoImN0MSIgPSBjKHN1YmRmY3QkY3QxLCBzdWJkZmN0JGN0MiksCiAgICAgICAgICAgICAgICAgICAgImN0MiIgPSBjKHN1YmRmY3QkY3QyLCBzdWJkZmN0JGN0MSkpCiAgaW50X2NvdW50c190b3RhbFtbbl1dID0gZGZjdAogIHBoZWF0bWFwOjpwaGVhdG1hcCh0YWJsZShkZmN0JGN0MSwgZGZjdCRjdDIpLCBtYWluID0gbikKICAKICBrZWVwID0gaW50ZXJfZGZbW25dXSRscjFfbmN0PD0xIHwgaW50ZXJfZGZbW25dXSRscjJfbmN0PD0xCiAgc3ViZGZjdCA9IHVuaXF1ZShpbnRlcl9kZltbbl1dW2tlZXAsYygxOjMsIDE1LDE2KV0pCiAgc3dwID0gc3ViZGZjdFssNV09PTEKICB0bXAgPSBzdWJkZmN0JGN0Mltzd3BdCiAgc3ViZGZjdCRjdDJbc3dwXSA9IHN1YmRmY3QkY3QxW3N3cF0KICBzdWJkZmN0JGN0MVtzd3BdID0gdG1wCiAgdG1wID0gc3ViZGZjdCRscjJfbmN0W3N3cF0KICBzdWJkZmN0JGxyMl9uY3Rbc3dwXSA9IHN1YmRmY3QkbHIxX25jdFtzd3BdCiAgc3ViZGZjdCRscjFfbmN0W3N3cF0gPSB0bXAKICBkdXAgPSBzdWJkZmN0W3N1YmRmY3QkbHIxX25jdD09MSAmIHN1YmRmY3QkbHIyX25jdD09MSxdCiAgdG1wID0gZHVwJGN0MgogIGR1cCRjdDIgPSBkdXAkY3QxCiAgZHVwJGN0MSA9IHRtcAogIHN1YmRmY3QgPSByYmluZChzdWJkZmN0LCBkdXApCiAgc3ViZGZjdCA9IHVuaXF1ZShzdWJkZmN0WywxOjNdKQogIGludF9jb3VudHNfZmlsdFtbbl1dID0gZGZjdAogIHBoZWF0bWFwOjpwaGVhdG1hcCh0YWJsZShzdWJkZmN0JGN0MSwgc3ViZGZjdCRjdDIpLCBtYWluID0gbikKfQpzYXZlUkRTKGludF9jb3VudHNfdG90YWwsIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9pbnRfY291bnRzX3RvdGFsLlJEUyIpCnNhdmVSRFMoaW50X2NvdW50c19maWx0LCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvaW50X2NvdW50c19maWx0LlJEUyIpCmBgYAoKR2V0IG5vbi11YmlxdWl0b3VzIGludGVyYWN0aW9ucwoKYGBge3J9CmludGVyX25vbnNzID0gc2V0ZGlmZih1bmlxdWUoYyhpbnRlcl9kZiRlbWJvbGlzZWQkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZiRyZWdlbmVyYXRpbmckaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uKSkKaW50ZXJfbm9uZW1iID0gc2V0ZGlmZih1bmlxdWUoYyhpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uKSkKaW50ZXJfbm9ucmVnID0gc2V0ZGlmZih1bmlxdWUoYyhpbnRlcl9kZiRlbWJvbGlzZWQkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSkKCmN0X2dfY29uZCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhpbnRlcl9kZilbMTozXSl7CiAgZGYgPSBpbnRlcl9kZltbbl1dW2ludGVyX2RmW1tuXV0kY3BkYl91bmlxdWUgfCAKICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZltbbl1dJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgYyhpbnRlcl9ub25zcywgaW50ZXJfbm9uZW1iLCBpbnRlcl9ub25yZWcpLF0KICAjZGYgPSBkZltkZiRscjFfbmN0PD0xIHwgZGYkbHIyX25jdDw9MSxdCiAgCiAgY3RzID0gdW5pcXVlKGMoZGYkY3QxLCBkZiRjdDIpKQogIGN0X2dfbGlzdCA9IGxpc3QoKQogIGZvcihjdCBpbiBjdHMpewogICAgY3RfZ19saXN0W1tjdF1dID0gZGF0YS5mcmFtZSgiaW50ZXJhY3QiID0gYyhhcy5jaGFyYWN0ZXIoZGYkaWRfY3BfaW50ZXJhY3Rpb25bZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkaWRfY3BfaW50ZXJhY3Rpb25bZGYkY3QyPT1jdF0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdlbmUiID0gYyhhcy5jaGFyYWN0ZXIoZGYkZ24xW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkZ24yW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX3RhcmdldCIgPSBjKGFzLmNoYXJhY3RlcihkZiRnbjJbZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkZ24xW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdCIgPSBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImN0X3RhcmdldCIgPSBjKGFzLmNoYXJhY3RlcihkZiRjdDJbZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkY3QxW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb25kIiA9IG4sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIH0KICBjdF9nX2NvbmRbW25dXSA9IHVuaXF1ZShSZWR1Y2UocmJpbmQsIGN0X2dfbGlzdCkpCn0KY3RfZ19jb25kID0gUmVkdWNlKHJiaW5kLCBjdF9nX2NvbmQpCmRpbShjdF9nX2NvbmQpCmxlbmd0aCh1bmlxdWUoY3RfZ19jb25kJGludGVyYWN0KSkKCgppbnRlcl9ub25zcyA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkZW1ib2xpc2VkX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZiRyZWdlbmVyYXRpbmdfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGludGVyX2RmJGhlYWx0aHlfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbikpCmludGVyX25vbmVtYiA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkaGVhbHRoeV9zaW1wJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkcmVnZW5lcmF0aW5nX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShpbnRlcl9kZiRlbWJvbGlzZWRfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbikpCmludGVyX25vbnJlZyA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkZW1ib2xpc2VkX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZiRoZWFsdGh5X3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShpbnRlcl9kZiRyZWdlbmVyYXRpbmdfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbikpCgpjdHNfZ19jb25kID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKGludGVyX2RmKVs0OjZdKXsKICBkZiA9IGludGVyX2RmW1tuXV1baW50ZXJfZGZbW25dXSRjcGRiX3VuaXF1ZSB8IAogICAgICAgICAgICAgICAgICAgICAgIGludGVyX2RmW1tuXV0kaWRfY3BfaW50ZXJhY3Rpb24gJWluJSBjKGludGVyX25vbnNzLCBpbnRlcl9ub25lbWIsIGludGVyX25vbnJlZyksXQogICNkZiA9IGRmW2RmJGxyMV9uY3Q8PTEgfCBkZiRscjJfbmN0PD0xLF0KICAKICBjdHMgPSB1bmlxdWUoYyhkZiRjdDEsIGRmJGN0MikpCiAgY3RfZ19saXN0ID0gbGlzdCgpCiAgZm9yKGN0IGluIGN0cyl7CiAgICBjdF9nX2xpc3RbW2N0XV0gPSBkYXRhLmZyYW1lKCJpbnRlcmFjdCIgPSBjKGFzLmNoYXJhY3RlcihkZiRpZF9jcF9pbnRlcmFjdGlvbltkZiRjdDE9PWN0XSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihkZiRpZF9jcF9pbnRlcmFjdGlvbltkZiRjdDI9PWN0XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ2VuZSIgPSBjKGFzLmNoYXJhY3RlcihkZiRnbjFbZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihkZiRnbjJbZGYkY3QyPT1jdF0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdlbmVfdGFyZ2V0IiA9IGMoYXMuY2hhcmFjdGVyKGRmJGduMltkZiRjdDE9PWN0XSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihkZiRnbjFbZGYkY3QyPT1jdF0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImN0IiA9IGN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3RfdGFyZ2V0IiA9IGMoYXMuY2hhcmFjdGVyKGRmJGN0MltkZiRjdDE9PWN0XSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihkZiRjdDFbZGYkY3QyPT1jdF0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbmQiID0gbiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgfQogIGN0c19nX2NvbmRbW25dXSA9IHVuaXF1ZShSZWR1Y2UocmJpbmQsIGN0X2dfbGlzdCkpCn0KY3RzX2dfY29uZCA9IFJlZHVjZShyYmluZCwgY3RzX2dfY29uZCkKZGltKGN0c19nX2NvbmQpCmxlbmd0aCh1bmlxdWUoY3RzX2dfY29uZCRpbnRlcmFjdCkpCgpjdF9nX2wgPSBsaXN0KCJjdHNfZ19jb25kIiA9IGN0c19nX2NvbmQsCiAgICAgICAgICAgICAgImN0X2dfY29uZCIgPSBjdF9nX2NvbmQpCmBgYAoKQW5ub3RhdGUgaW50ZXJhY3Rpb25zCgpgYGB7cn0KaW50ZXJfYW5ub3QgPSByZWFkLmNzdigicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9pbnRlcl91bmlxdWU1LmNzdiIsIGhlYWRlciA9IFQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQojeHh4ID0gbWVyZ2UodW5pcXVlKHJiaW5kKGN0X2dfY29uZFssMTozXSwgY3RzX2dfY29uZFssMTozXSkpLCAKIyAgICAgICAgICAgIHVuaXF1ZShpbnRlcl9hbm5vdFssYygxLDQpXSksIGJ5ID0gMSwgYWxsLnggPSBUKQojd3JpdGUuY3N2KHh4eCwgZmlsZSA9ICJpbnRlcl91bmlxdWU1LmNzdiIsIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IFQsIHF1b3RlID0gRikKCmludGVyX2Fubm90ID0gdW5pcXVlKGludGVyX2Fubm90WyxjKDEsNCldKQoKZGVzY3JpcHRpb24gPSBzdHJzcGxpdChpbnRlcl9hbm5vdCRkZXNjcmlwdGlvbiwgIjsiKQppbnRlcl9kZXMgPSBsYXBwbHkoMTpsZW5ndGgoZGVzY3JpcHRpb24pLCAKICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHJlcChpbnRlcl9hbm5vdCRpbnRlcmFjdFt4XSwgbGVuZ3RoKGRlc2NyaXB0aW9uW1t4XV0pKSkKaW50ZXJfYW5ub3QgPSBkYXRhLmZyYW1lKCJpbnRlciIgPSB1bmxpc3QoaW50ZXJfZGVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICJmdW5jdCIgPSB1bmxpc3QoZGVzY3JpcHRpb24pKQoKaW50ZXJfYW5ub3QkZnVuY3RbaW50ZXJfYW5ub3QkZnVuY3Q9PSJpbnRlcmNlbGx1bGFyIGFkaGVzaW9uIl0gPSAiYWRoZXNpb24iCmludGVyX2Fubm90JGZ1bmN0W2ludGVyX2Fubm90JGZ1bmN0PT0iYW50aWJvZHkgcmVndWxhdGlvbiJdID0gImltbXVuZSByZWd1bGF0aW9uIgppbnRlcl9hbm5vdCRmdW5jdFtpbnRlcl9hbm5vdCRmdW5jdD09ImFudGlnZW4gcHJlc2VudGF0aW9uIl0gPSAiaW1tdW5lIGFjdGl2aXR5IgoKd3JpdGUuY3N2KGludGVyX2Fubm90LCBmaWxlID0gImRhdGEvaW50ZXJhY3Rpb25fYW5ub3RhdGlvbjIuY3N2IiwgCiAgICAgICAgICByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBULCBxdW90ZSA9IEYpCmNvbG5hbWVzKGludGVyX2Fubm90KSA9IGMoImludGVyYWN0IiwgImRlc2NyaXB0aW9uIikKCmN0X2dfY29uZF9hbm5fbCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhjdF9nX2wpKXsKICBjdF9nX2NvbmRfYW5uID0gbWVyZ2UoY3RfZ19sW1tuXV0sIGludGVyX2Fubm90LCBieSA9IDEsIGFsbC54ID0gVCkKICBjdF9nX2NvbmRfYW5uID0gdW5pcXVlKGN0X2dfY29uZF9hbm5bLGMoMSw0LDUsNiw3KV0pCiAgCiAgY3RfZ19jb25kX2FubiA9IGRhdGEuZnJhbWUoImludGVyYWN0aW9ucyIgPSByZXAoYXMuY2hhcmFjdGVyKGN0X2dfY29uZF9hbm4kaW50ZXJhY3QpLDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjZWxsdHlwZXMiID0gYyhhcy5jaGFyYWN0ZXIoY3RfZ19jb25kX2FubiRjdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihjdF9nX2NvbmRfYW5uJGN0X3RhcmdldCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iID0gcmVwKGFzLmNoYXJhY3RlcihjdF9nX2NvbmRfYW5uJGNvbmQpLDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiIgPSByZXAoYXMuY2hhcmFjdGVyKGN0X2dfY29uZF9hbm4kZGVzY3JpcHRpb24pLDIpKQogIGN0X2dfY29uZF9hbm4kY29uZGl0aW9uID0gZmFjdG9yKHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoY3RfZ19jb25kX2FubiRjb25kaXRpb24sICJfIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgeFsxXSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJoZWFsdGh5IiwgImVtYm9saXNlZCIsICJyZWdlbmVyYXRpbmciKSkKICAKICBwbHRfYmFyX2Z1bmMgPSBnZ3Bsb3QoY3RfZ19jb25kX2FubiwgYWVzKHggPSBjb25kaXRpb24sIGZpbGwgPSBjb25kaXRpb24pKSsKICAgIGZhY2V0X2dyaWQoZGVzY3JpcHRpb25+Y2VsbHR5cGVzLCBzY2FsZXMgPSAiZnJlZV95IikrCiAgICBnZW9tX2JhcigpKwogICAgdGhlbWVfYncoKSsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgdmp1c3QgPSAxKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICBwZGYocGFzdGUwKCJyZXN1bHRzL2NlbGxfY29tbS91cGR0LyIsIG4sICJfcGx0X2Jhcl9mdW5jLnBkZiIpLCBoZWlnaHQgPSAyMCwgd2lkdGggPSA1MCkKICBwcmludChwbHRfYmFyX2Z1bmMpCiAgZGV2Lm9mZigpCiAgCiAgY3RfZ19jb25kX2Fubl9sW1tuXV0gPSBjdF9nX2NvbmRfYW5uCiAgc2F2ZVJEUyhjdF9nX2NvbmRfYW5uLCBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL2NlbGxfY29tbS91cGR0LyIsIG4sICJfYW5uLlJEUyIpKQp9CmBgYAoKR2V0IGV4cHJlc3Npb24gZm9yIGVhY2ggaW50ZXJhY3Rpb24gaW4gZWFjaCBjb25kaXRpb24KCmBgYHtyfQpleHBfbGlzdCA9IGxpc3QoKQppbnRfZ3JvdXBzID0gbGlzdCgiY3RzX2dfY29uZCIgPSB1bmlxdWUoYyhzaWdfbWVhbnNfbmFtZXNfbCRoZWFsdGh5X3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ19tZWFuc19uYW1lc19sJGVtYm9saXNlZF9zaW1wJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdfbWVhbnNfbmFtZXNfbCRyZWdlbmVyYXRpbmdfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICAiY3RfZ19jb25kIiA9IHVuaXF1ZShjKHNpZ19tZWFuc19uYW1lc19sJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ19tZWFuc19uYW1lc19sJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnX21lYW5zX25hbWVzX2wkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSkpCmZvcihuIGluIG5hbWVzKHNpZ19tZWFuc19uYW1lc19sKSl7CiAgZGYgPSBzaWdfbWVhbnNfbmFtZXNfbFtbbl1dWyxjKDEsNTo2LDEzOm5jb2woc2lnX21lYW5zX25hbWVzX2xbW25dXSkpXQogIAogICNjb2xuYW1lcyhkZikgPSBnc3ViKCIuIiwgIiAiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJnZC5ULmNlbGxzIiwgImdkLVQgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJCLmNlbGxzIiwgIkIgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJEaXZpZGluZy5lbmRvdGhlbGlhbC5jZWxscyIsICJEaXZpZGluZyBlbmRvdGhlbGlhbCBjZWxscyIsIAogICAgICAgICAgICAgICAgICAgICAgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiRGl2aWRpbmcuY0RDcyIsICJEaXZpZGluZyBjRENzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiSW5maWx0cmF0aW5nLk5LLmNlbGxzIiwgIkluZmlsdHJhdGluZyBOSyBjZWxscyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk1hY3JvcGhhZ2VzLi5IRVM0Li4iLCAiTWFjcm9waGFnZXMgKEhFUzQrKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoImFjdGl2YXRlZC5EQ3MiLCAiYWN0aXZhdGVkIERDcyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIlBlcmljZW50cmFsLkxTRUMiLCAiUGVyaWNlbnRyYWwgTFNFQyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk1pZHpvbmFsLkxTRUMiLCAiTWlkem9uYWwgTFNFQyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIlBlcmlwb3J0YWwuTFNFQyIsICJQZXJpcG9ydGFsIExTRUMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJQZXJpcG9ydGFsLkxTRUMiLCAiUGVyaXBvcnRhbCBMU0VDIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTksuY2VsbHMuMSIsICJOSyBjZWxscyAxIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTksuY2VsbHMuMiIsICJOSyBjZWxscyAyIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTksuY2VsbHMuMyIsICJOSyBjZWxscyAzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTksuY2VsbHMiLCAiTksgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJDRDguYWIuVC5jZWxscy4xIiwgIkNEOCBhYi1UIGNlbGxzIDEiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJDRDguYWIuVC5jZWxscy4yIiwgIkNEOCBhYi1UIGNlbGxzIDIiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJDRDguYWIuVC5jZWxscy4zIiwgIkNEOCBhYi1UIGNlbGxzIDMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJDRDguYWIuVC5jZWxscyIsICJDRDggYWItVCBjZWxscyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk5haXZlLkNENC4uVC5jZWxscyIsICJOYWl2ZSBDRDQrIFQgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJhYi5ULmNlbGxzLi5zdHJlc3MuIiwgImFiLVQgY2VsbHMgKHN0cmVzcykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNb25vY3l0ZXMuLnNlY3JldG9yeS4iLCAiTW9ub2N5dGVzIChzZWNyZXRvcnkpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTW9ub2N5dGVzLi5UUkVNMi4uQ0Q5Li4iLCAiTW9ub2N5dGVzIChUUkVNMisgQ0Q5KykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNb25vY3l0ZXMuLklHU0YyMS4uR1BSMzQuLiIsICJNb25vY3l0ZXMgKElHU0YyMSsgR1BSMzQrKSIsIAogICAgICAgICAgICAgICAgICAgICAgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uc3RyZXNzLiIsICJMU0VDIChzdHJlc3MpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4ucmVtb2RlbGxpbmcuIiwgIkxTRUMgKHJlbW9kZWxsaW5nKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkxTRUMuLmludGVyZmVyb24uIiwgIkxTRUMgKGludGVyZmVyb24pIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uaGlnaC5NVC4yLiIsICJMU0VDIChoaWdoIE1UIDIpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uaGlnaC5NVC4xLiIsICJMU0VDIChoaWdoIE1UIDEpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uaGlnaC5NVC4iLCAiTFNFQyAoaGlnaCBNVCkiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJMU0VDLi5mZW5lc3RyLi4iLCAiTFNFQyAoZmVuZXN0ci4pIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiS3VwZmZlci5jZWxscy4uU1VDTlIxLi4iLCAiS3VwZmZlciBjZWxscyAoU1VDTlIxKykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJJZ0cuLlBsYXNtYS5jZWxscyIsICJJZ0crIFBsYXNtYSBjZWxscyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIklnQS4uUGxhc21hLmNlbGxzIiwgIklnQSsgUGxhc21hIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiRUMubm9uLkxTRUMiLCAiRUMgbm9uLUxTRUMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJEaXZpZGluZy5ULk5LLmNlbGxzIiwgIkRpdmlkaW5nIFQvTksgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJLdXBmZmVyLmNlbGxzIiwgIkt1cGZmZXIgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJUUk0uY2VsbHMiLCAiVFJNIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTUFJVC5jZWxscy4xIiwgIk1BSVQgY2VsbHMgMSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk1BSVQuY2VsbHMuMiIsICJNQUlUIGNlbGxzIDIiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNQUlULmNlbGxzIiwgIk1BSVQgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJMeW1waGF0aWMuRUMiLCAiTHltcGhhdGljIEVDIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiU3RlbGxhdGUuY2VsbHMiLCAiU3RlbGxhdGUgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICAjIGhlYWQoY29sbmFtZXMoZGYpLCA2MCkKICAKICBkZiA9IHJlc2hhcGUyOjptZWx0KGRmLCBpZC52YXJzID0gMTozKQogIGRmJHZhbHVlW2lzLm5hKGRmJHZhbHVlKV0gPSAwCiAgZGYgPSBkZiAlPiUKICAgZ3JvdXBfYnkoaWRfY3BfaW50ZXJhY3Rpb24sIGdlbmVfYSwgZ2VuZV9iLCB2YXJpYWJsZSkgJT4lCiAgIHN1bW1hcmlzZSh2YWx1ZT0obWVhbih2YWx1ZSkpLCAuZ3JvdXBzID0gImtlZXAiKQogIGRmID0gYXMuZGF0YS5mcmFtZShkZikKICAKICBucyA9IGlmKGdyZXBsKCJzaW1wIiwgbikpICJjdHNfZ19jb25kIiBlbHNlICJjdF9nX2NvbmQiCiAgY3RfZ19jb25kX2FubiA9IGN0X2dfY29uZF9hbm5fbFtbbnNdXQogIHN1Yl9kZiA9IGRmW2RmJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgY3RfZ19jb25kX2FubiRpbnRlcmFjdGlvbnMsXQogIHN1Yl9kZiA9IG1lcmdlKGN0X2dfbFtbbnNdXSwgc3ViX2RmLCBhbGwgPSBULCBieSA9IDEpCiAgc3ViX2RmID0gc3ViX2RmW3N1Yl9kZiRpbnRlcmFjdCAlaW4lIGludF9ncm91cHNbW25zXV0sXQogIHN1Yl9kZiR2YXJpYWJsZSA9IGFzLmNoYXJhY3RlcihzdWJfZGYkdmFyaWFibGUpCiAgc3ViX2RmJHZhbHVlW2lzLm5hKHN1Yl9kZiR2YWx1ZSldID0gMAogIAogIGN0MSA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoc3ViX2RmJHZhcmlhYmxlLCAiLiIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHhbMV0pKQogIGN0MiA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoc3ViX2RmJHZhcmlhYmxlLCAiLiIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHhbMl0pKQogIGtlZXAgPSBzYXBwbHkoMTpucm93KHN1Yl9kZiksIGZ1bmN0aW9uKHgpIChzdWJfZGYkY3RbeF09PWN0MVt4XSAmIHN1Yl9kZiRjdF90YXJnZXRbeF09PWN0Mlt4XSkgfAogICAgICAgICAgICAgICAgICAoc3ViX2RmJGN0W3hdPT1jdDJbeF0gJiBzdWJfZGYkY3RfdGFyZ2V0W3hdPT1jdDFbeF0pKQogIGV4cF9saXN0W1tuXV0gPSBzdWJfZGZba2VlcCxjKDE6NiwxMCldCn0KZm9yKG4gaW4gbmFtZXMoZXhwX2xpc3QpKXsKICBleHBfbGlzdFtbbl1dID0gZXhwX2xpc3RbW25dXVshaXMubmEoZXhwX2xpc3RbW25dXSRpbnRlcmFjdCksXQp9CgpjdF9pbnRfZXhwX2wgPSBsaXN0KCkKZm9yKHNpbXAgaW4gYyhULCBGKSl7CiAgbl91c2UgPSBuYW1lcyhleHBfbGlzdClbZ3JlcGwoInNpbXAiLCBuYW1lcyhleHBfbGlzdCkpPT1zaW1wXQogICNjdF9pbnRfZXhwID0gY2JpbmQoZXhwX2xpc3RbW25fdXNlWzFdXV0sIGV4cF9saXN0W1tuX3VzZVszXV1dJHZhbHVlLCBleHBfbGlzdFtbbl91c2VbM11dXSR2YWx1ZSkKICBjdF9pbnRfZXhwID0gbWVyZ2UoZXhwX2xpc3RbW25fdXNlWzFdXV0sIGV4cF9saXN0W1tuX3VzZVsyXV1dLCBieSA9IDE6NikKICBjdF9pbnRfZXhwID0gbWVyZ2UoY3RfaW50X2V4cCwgZXhwX2xpc3RbW25fdXNlWzNdXV0sIGJ5ID0gMTo2KQogIGN0X2ludF9leHAkdmFsdWUueFtpcy5uYShjdF9pbnRfZXhwJHZhbHVlLngpXSA9IGN0X2ludF9leHAkdmFsdWUueVtpcy5uYShjdF9pbnRfZXhwJHZhbHVlLnkpXSA9IGN0X2ludF9leHAkdmFsdWVbaXMubmEoY3RfaW50X2V4cCR2YWx1ZSldID0gMAogIGNvbG5hbWVzKGN0X2ludF9leHApWzc6OV0gPSBjKCJoZWFsdGh5X2V4cCIsICJlbWJvbGlzZWRfZXhwIiwgInJlZ2VuZXJhdGluZ19leHAiKQogIGN0X2ludF9leHAgPSBjdF9pbnRfZXhwW2N0X2ludF9leHAkaGVhbHRoeV9leHA+MCB8IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RfaW50X2V4cCRlbWJvbGlzZWRfZXhwPjAgfCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN0X2ludF9leHAkcmVnZW5lcmF0aW5nX2V4cD4wLF0KCiAgdHVwX2xpc3QgPSBsaXN0KCkKICBrZWVwX3JvdyA9IGMoKQogIGZvcihpIGluIDE6bnJvdyhjdF9pbnRfZXhwKSl7CiAgICB0dXAxID0gcGFzdGUoYyhjdF9pbnRfZXhwJGdlbmVbaV0sIGN0X2ludF9leHAkZ2VuZV90YXJnZXRbaV0sIGN0X2ludF9leHAkY3RbaV0sIAogICAgICAgICAgICAgICAgICAgY3RfaW50X2V4cCRjdF90YXJnZXRbaV0sIGN0X2ludF9leHAkY29uZFtpXSksIGNvbGxhcHNlID0gIiAiKQogICAgdHVwMiA9IHBhc3RlKGMoY3RfaW50X2V4cCRnZW5lX3RhcmdldFtpXSwgY3RfaW50X2V4cCRnZW5lW2ldLCBjdF9pbnRfZXhwJGN0X3RhcmdldFtpXSwgCiAgICAgICAgICAgICAgICAgICBjdF9pbnRfZXhwJGN0W2ldLCBjdF9pbnRfZXhwJGNvbmRbaV0pLCBjb2xsYXBzZSA9ICIgIikKICAgIGlmKCEodHVwMSAlaW4lIHR1cF9saXN0KSAmICEodHVwMiAlaW4lIHR1cF9saXN0KSl7CiAgICAgIHR1cF9saXN0ID0gYyh0dXBfbGlzdCwgdHVwMSwgdHVwMikKICAgICAga2VlcF9yb3cgPSBjKGtlZXBfcm93LCBUKQogICAgfSBlbHNlewogICAgICBrZWVwX3JvdyA9IGMoa2VlcF9yb3csIEYpCiAgICB9CiAgfQogIGN0X2ludF9leHAgPSBjdF9pbnRfZXhwW2tlZXBfcm93LF0KICAKICBjdF9pbnRfZXhwID0gbWVyZ2UoY3RfaW50X2V4cCwgdW5pcXVlKGN0X2dfY29uZF9hbm5bLGMoMSw0KV0pLCBieSA9IDEsIGFsbCA9IFQpCiAgbm4gPSBpZihzaW1wKSAic2ltcCIgZWxzZSAiYWxsIgogIGN0X2ludF9leHBfbFtbbm5dXSA9IGN0X2ludF9leHAKICBjdF9pbnRfZXhwX2xbW25uXV0gPSBjdF9pbnRfZXhwX2xbW25uXV1bY29tcGxldGUuY2FzZXMoY3RfaW50X2V4cF9sW1tubl1dKSxdCiAgCiAgY3RfaW50X2V4cF9sW1tubl1dJGNvbmQgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KGN0X2ludF9leHBfbFtbbm5dXSRjb25kLCAiXyIpLCBmdW5jdGlvbih4KSB4WzFdKSkKfQoKc2F2ZVJEUyhjdF9pbnRfZXhwX2wsIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9pbnRlcmFjdF9jZWxsdHlwZV9leHBfZ3JvdXBfbGlzdC5SRFMiKQpgYGAKClZhcmlhYmlsaXR5IG9mIGludGVyYWN0aW9ucwoKYGBge3J9CmludGVyX2Fubm90ID0gcmVhZC5jc3YoImRhdGEvaW50ZXJhY3Rpb25fYW5ub3RhdGlvbjIuY3N2IiwgaGVhZGVyID0gVCkKaW50ZXJfYW5ub3QkZnVuY3RbZ3JlcGwoImltbSIsIGludGVyX2Fubm90JGZ1bmN0KV0gPSAiaW1tdW5lIgppbnRlcl9hbm5vdCRmdW5jdFtncmVwbCgiaW5mbGFtIiwgaW50ZXJfYW5ub3QkZnVuY3QpXSA9ICJpbW11bmUiCmdyID0gaW50ZXJfYW5ub3QkZnVuY3QKbmFtZXMoZ3IpID0gaW50ZXJfYW5ub3QkaW50ZXIKZ3JfbGlzdCA9IHRhcHBseShpbnRlcl9hbm5vdCRpbnRlciwgaW50ZXJfYW5ub3QkZnVuY3QsIGZ1bmN0aW9uKHgpIHgpCgpoZXJfYWxsaW50X2wgPSBsaXN0KCkKZm9yKHNpbXAgaW4gYyhULCBGKSl7CiAgbl91c2UgPSBuYW1lcyhyZWZvcm1fbGlzdClbZ3JlcGwoInNpbXAiLCBuYW1lcyhyZWZvcm1fbGlzdCkpPT1zaW1wXQogIAogIGhlX2FsbGludCA9IG1lcmdlKHJlZm9ybV9saXN0W1tuX3VzZVsxXV1dWywxOjZdLCByZWZvcm1fbGlzdFtbbl91c2VbMl1dXVssMTo2XSwgYnkgPSAxOjMsIGFsbCA9IFQpCiAgaGVfYWxsaW50JGxyMS54W2lzLm5hKGhlX2FsbGludCRscjEueCldID0gaGVfYWxsaW50JGxyMS55W2lzLm5hKGhlX2FsbGludCRscjEueCldCiAgaGVfYWxsaW50JGxyMi54W2lzLm5hKGhlX2FsbGludCRscjIueCldID0gaGVfYWxsaW50JGxyMi55W2lzLm5hKGhlX2FsbGludCRscjIueCldCiAgaGVfYWxsaW50ID0gaGVfYWxsaW50WyxjKDE6Niw5KV0KICBoZXJfYWxsaW50ID0gbWVyZ2UoaGVfYWxsaW50LCByZWZvcm1fbGlzdFtbbl91c2VbM11dXVssMTo2XSwgYnkgPSAxOjMsIGFsbCA9IFQpCiAgaGVyX2FsbGludCRscjEueFtpcy5uYShoZXJfYWxsaW50JGxyMS54KV0gPSBoZXJfYWxsaW50JGxyMVtpcy5uYShoZXJfYWxsaW50JGxyMS54KV0KICBoZXJfYWxsaW50JGxyMi54W2lzLm5hKGhlcl9hbGxpbnQkbHIyLngpXSA9IGhlcl9hbGxpbnQkbHIyW2lzLm5hKGhlcl9hbGxpbnQkbHIyLngpXQogIGhlcl9hbGxpbnQgPSBoZXJfYWxsaW50WyxjKDE6NywxMCldCiAgaGVyX2FsbGludFtpcy5uYShoZXJfYWxsaW50KV0gPSAwCiAgY29sbmFtZXMoaGVyX2FsbGludClbNDo4XSA9IGMoImxyMSIsICJscjIiLCAiaGVhbHRoeV9leHAiLCAiZW1ib2xpc2VkX2V4cCIsICJyZWdlbmVyYXRpbmdfZXhwIikKICAKICAjIGNvdW50IG9jY3VycmVuY2VzIHBlciBjb25kaXRpb24uIHRoaXMgd29ya3MgYmMgd2UncmUgYWxyZWFkeSB3b3JraW5nIHdpdGggc2lnIG1lYW5zCiAgaGVyX2FsbGludCA9IG1lcmdlKGhlcl9hbGxpbnQsIGRhdGEuZnJhbWUodGFibGUoaGVyX2FsbGludCRpZF9jcF9pbnRlcmFjdGlvbltoZXJfYWxsaW50JGhlYWx0aHlfZXhwPjBdKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gMSwgYWxsLnggPSBUKQogIGhlcl9hbGxpbnQgPSBtZXJnZShoZXJfYWxsaW50LCBkYXRhLmZyYW1lKHRhYmxlKGhlcl9hbGxpbnQkaWRfY3BfaW50ZXJhY3Rpb25baGVyX2FsbGludCRlbWJvbGlzZWRfZXhwPjBdKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gMSwgYWxsLnggPSBUKQogIGhlcl9hbGxpbnQgPSBtZXJnZShoZXJfYWxsaW50LCAKICAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZSh0YWJsZShoZXJfYWxsaW50JGlkX2NwX2ludGVyYWN0aW9uW2hlcl9hbGxpbnQkcmVnZW5lcmF0aW5nX2V4cD4wXSkpLCAKICAgICAgICAgICAgICAgICAgICAgYnkgPSAxLCBhbGwueCA9IFQpCiAgY29sbmFtZXMoaGVyX2FsbGludClbOToxMV0gPSBjKCJoZWFsdGh5X24iLCAiZW1ib2xpc2VkX24iLCAicmVnZW5lcmF0aW5nX24iKQogIGhlcl9hbGxpbnRbaXMubmEoaGVyX2FsbGludCldID0gMAogIGhlcl9hbGxpbnQkY3RfcGFpciA9IGZhY3RvcihwYXN0ZTAoaGVyX2FsbGludCRjdDEsICJfIiwgaGVyX2FsbGludCRjdDIpKQogIAogIGNvbWJfY29uZCA9IGNvbWJuKGNvbG5hbWVzKGhlcl9hbGxpbnQpWzY6OF0sMikKICBjb2xuYW1lcyhjb21iX2NvbmQpID0gYygiaGUiLCAiaHIiLCAiZXIiKQogIGZvcihpIGluIGNvbG5hbWVzKGNvbWJfY29uZCkpewogICAgcGxvdF9kZiA9IGhlcl9hbGxpbnRbaGVyX2FsbGludFssY29tYl9jb25kWzEsaV1dPjAgfCBoZXJfYWxsaW50Wyxjb21iX2NvbmRbMixpXV0+MCwxOjEyXQogICAgCiAgICBleHBfZGYxID0gcmVzaGFwZTI6OmRjYXN0KHBsb3RfZGYsIGZvcm11bGEgPSBpZF9jcF9pbnRlcmFjdGlvbiB+IGN0X3BhaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS52YXIgPSBjb21iX2NvbmRbMSxpXSwgZmlsbCA9IDApCiAgICByb3duYW1lcyhleHBfZGYxKSA9IGV4cF9kZjFbLDFdCiAgICBleHBfZGYxID0gZXhwX2RmMVssLTFdPjAKICAgIGV4cF9kZjIgPSByZXNoYXBlMjo6ZGNhc3QocGxvdF9kZiwgZm9ybXVsYSA9IGlkX2NwX2ludGVyYWN0aW9uIH4gY3RfcGFpciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLnZhciA9IGNvbWJfY29uZFsyLGldLCBmaWxsID0gMCkKICAgIHJvd25hbWVzKGV4cF9kZjIpID0gZXhwX2RmMlssMV0KICAgIGV4cF9kZjIgPSBleHBfZGYyWywtMV0+MAogICAgCiAgICBwbG90X2RmID0gbWVyZ2UocGxvdF9kZiwgc2FwcGx5KHJvd25hbWVzKGV4cF9kZjEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgaW5mb3RoZW86Om11dGluZm9ybWF0aW9uKGV4cF9kZjFbeCxdLCBleHBfZGYyW3gsXSkpLAogICAgICAgICAgICAgICAgICAgIGJ5LnggPSAxLCBieS55ID0gMCwgYWxsLnggPSBUKQogICAgcGxvdF9kZiA9IG1lcmdlKHBsb3RfZGYsIHNhcHBseShyb3duYW1lcyhleHBfZGYxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIGUxMDcxOjpoYW1taW5nLmRpc3RhbmNlKGV4cF9kZjFbeCxdLCBleHBfZGYyW3gsXSkpLAogICAgICAgICAgICAgICAgICAgIGJ5LnggPSAxLCBieS55ID0gMCwgYWxsLnggPSBUKQogICAgcGxvdF9kZiA9IG1lcmdlKHBsb3RfZGYsIHNhcHBseShyb3duYW1lcyhleHBfZGYxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIGUxMDcxOjpoYW1taW5nLmRpc3RhbmNlKGV4cF9kZjFbeCxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfZGYyW3gsXSkvc3VtKGV4cF9kZjFbeCxdIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cF9kZjJbeCxdKSksCiAgICAgICAgICAgICAgICAgICAgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCiAgICBwbG90X2RmID0gbWVyZ2UocGxvdF9kZiwgc2FwcGx5KHJvd25hbWVzKGV4cF9kZjEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgc3VtKGV4cF9kZjFbeCxdIHwgZXhwX2RmMlt4LF0pKSwKICAgICAgICAgICAgICAgICAgICBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKICAgIAogICAgY29sbmFtZXMocGxvdF9kZilbMTM6MTZdID0gcGFzdGUwKGMoIm11dEluZm9fIiwgImhhbW1fIiwgImhhbW1Ob3JtXyIsICJ0b3RfIiksIGkpCiAgICAKICAgIGhlcl9hbGxpbnQgPSBtZXJnZShoZXJfYWxsaW50LCB1bmlxdWUocGxvdF9kZlssYygxLDEzOjE2KV0pLCBhbGwueCA9IFQsIGJ5ID0gMSkKICB9CiAgaGVyX2FsbGludCRtdXRJbmZvX2VyW2lzLm5hKGhlcl9hbGxpbnQkbXV0SW5mb19lcildID0gMQogIGhlcl9hbGxpbnQkbXV0SW5mb19ocltpcy5uYShoZXJfYWxsaW50JG11dEluZm9faHIpXSA9IDEKICBoZXJfYWxsaW50JG11dEluZm9faGVbaXMubmEoaGVyX2FsbGludCRtdXRJbmZvX2hlKV0gPSAxCiAgaGVyX2FsbGludCR0b3RfaGVbaXMubmEoaGVyX2FsbGludCR0b3RfaGUpXSA9IDAKICBoZXJfYWxsaW50JHRvdF9ocltpcy5uYShoZXJfYWxsaW50JHRvdF9ocildID0gMAogIGhlcl9hbGxpbnQkdG90X2VyW2lzLm5hKGhlcl9hbGxpbnQkdG90X2VyKV0gPSAwCiAgaGVyX2FsbGludCRoYW1tX2VyW2lzLm5hKGhlcl9hbGxpbnQkaGFtbV9lcildID0gMAogIGhlcl9hbGxpbnQkaGFtbV9ocltpcy5uYShoZXJfYWxsaW50JGhhbW1faHIpXSA9IDAKICBoZXJfYWxsaW50JGhhbW1faGVbaXMubmEoaGVyX2FsbGludCRoYW1tX2hlKV0gPSAwCiAgaGVyX2FsbGludCRoYW1tTm9ybV9lcltpcy5uYShoZXJfYWxsaW50JGhhbW1Ob3JtX2VyKV0gPSAwCiAgaGVyX2FsbGludCRoYW1tTm9ybV9oZVtpcy5uYShoZXJfYWxsaW50JGhhbW1Ob3JtX2hlKV0gPSAwCiAgaGVyX2FsbGludCRoYW1tTm9ybV9ocltpcy5uYShoZXJfYWxsaW50JGhhbW1Ob3JtX2hyKV0gPSAwCiAgCiAgaGVyX2FsbGludCRkaWZmX25faGUgPSBoZXJfYWxsaW50JGVtYm9saXNlZF9uLWhlcl9hbGxpbnQkaGVhbHRoeV9uCiAgaGVyX2FsbGludCRkaWZmX25faHIgPSBoZXJfYWxsaW50JHJlZ2VuZXJhdGluZ19uLWhlcl9hbGxpbnQkaGVhbHRoeV9uCiAgaGVyX2FsbGludCRkaWZmX25fZXIgPSBoZXJfYWxsaW50JHJlZ2VuZXJhdGluZ19uLWhlcl9hbGxpbnQkZW1ib2xpc2VkX24KICAKICAjIHBsb3QgdG90IHZzIG11dHVhbAogIHBsb3RfZGYgPSB1bmlxdWUoaGVyX2FsbGludFssYygiaWRfY3BfaW50ZXJhY3Rpb24iLCBwYXN0ZTAoYygibXV0SW5mb18iLCAidG90XyIsICJkaWZmX25fIiksIGkpKV0pCiAgcGx0ID0gZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0gdG90X2VyLCB5ID0gbXV0SW5mb19lciooZGlmZl9uX2VyL2FicyhkaWZmX25fZXIpKSkpKwogICAgZ2VvbV9iaW4yZCgpKwogICAgc2NhbGVfeF9sb2cxMCgpKwogICAgdGhlbWVfYncoKSsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCiAgcHJpbnQocGx0KQogIAogIG5uID0gaWYoc2ltcCkgInNpbXAiIGVsc2UgImFsbCIKICBoZXJfYWxsaW50X2xbW25uXV0gPSBoZXJfYWxsaW50Cn0KCnNhdmVSRFMoaGVyX2FsbGludF9sLCBmaWxlID0gIi4vcmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9pbnRlcmFjdGlvbnNfbXV0SW5mb19jb25kQ29tcC5SRFMiKQpgYGAKCkdTRUEgb2YgaW50ZXJhY3Rpb25zIHVzaW5nIG11dHVhbCBpbmZvcm1hdGlvbgoKYGBge3J9CmZvcihuIGluIG5hbWVzKGhlcl9hbGxpbnRfbCkpewogIGhlcl9hbGxpbnQgPSBoZXJfYWxsaW50X2xbW25dXQogIAogIGNvbWJfY29uZCA9IGNvbWJuKGNvbG5hbWVzKGhlcl9hbGxpbnQpWzY6OF0sMikKICBjb2xuYW1lcyhjb21iX2NvbmQpID0gYygiaGUiLCAiaHIiLCAiZXIiKQogIGdzZWFfbGlzdCA9IGxpc3QoKQogIGZvcihpIGluIGNvbG5hbWVzKGNvbWJfY29uZCkpewogICAgdmFscyA9IHVuaXF1ZShoZXJfYWxsaW50WyxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsIHBhc3RlMCgibXV0SW5mb18iLCBpKSldKQogICAgdmFsczIgPSB2YWxzWyxwYXN0ZTAoIm11dEluZm9fIiwgaSldIAogICAgbmFtZXModmFsczIpID0gdmFscyRpZF9jcF9pbnRlcmFjdGlvbgogICAgCiAgICBzZXQuc2VlZCgxKQogICAgZ3NlYV9saXN0W1tpXV0gPSBsaWdlcjo6YnVsay5nc2VhKDEtdmFsczIsIHNldC5saXN0ID0gZ3JfbGlzdCwgbi5yYW5kID0gMTAwMDAwMCkKICAgIGdzZWFfbGlzdFtbaV1dJGdyb3VwID0gcm93bmFtZXMoZ3NlYV9saXN0W1tpXV0pCiAgICBnc2VhX2xpc3RbW2ldXSRjb21wID0gaQogIH0KICAKICBsaWdlcjo6Z3NlYSgxLXZhbHMyLCBnZW5lc2V0ID0gZ3JfbGlzdCRpbW11bmUsIHBsb3QgPSBUKQogIAogICMgcGxvdCBHU0VBIGVucmljaG1lbnQKICBwbG90X2RmID0gUmVkdWNlKHJiaW5kLCBnc2VhX2xpc3QpCiAgcGxvdF9kZiRxLnZhbCA9IC1sb2cxMChwbG90X2RmJHEudmFsKzAuMDAwMDAwNSkqKHBsb3RfZGYkc3Njb3JlL2FicyhwbG90X2RmJHNzY29yZSkpCiAgcGxvdF9kZiRjb21wID0gZmFjdG9yKHBsb3RfZGYkY29tcCwgbGV2ZWxzID0gcmV2KGMoImhlIiwiaHIiLCJlciIpKSkKICBtID0gdGFwcGx5KHBsb3RfZGYkcS52YWwsIHBsb3RfZGYkZ3JvdXAsbWVhbikKICBwbG90X2RmJGdyb3VwID0gZmFjdG9yKHBsb3RfZGYkZ3JvdXAsIGxldmVscyA9IG5hbWVzKG0pW29yZGVyKG0sIGRlY3JlYXNpbmcgPSBGKV0pCiAgcGx0ID0gZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0gcS52YWwsIHkgPSBncm91cCwgZmlsbCA9IGNvbXApKSsKICAgIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikrCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKGxvZzEwKDAuMDUpLCAtbG9nMTAoMC4wNSkpLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMCkpKwogICAgbGFicyh4ID0gInEtdmFsdWUgeCBzY29yZSBzaWduYWwiLCB5ID0gImludGVyYWN0aW9uIHR5cGUiLCBmaWxsID0gImNvbXBhcmlzb24iKSsKICAgIHRoZW1lX2J3KCkrCiAgICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDcpLAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gNy41KSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMCwxKSwKICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygtMC4wNSwxLjA1KSwKICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNy41KSwKICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgICAgICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oMCwwLDAsMCksCiAgICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuMzUsICJjbSIpKQogIHByaW50KHBsdCkKICAKICBzYXZlUkRTKGdzZWFfbGlzdCwgZmlsZSA9IHBhc3RlMCgiLi9yZXN1bHRzL2NlbGxfY29tbS91cGR0LyIsIG4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdzZWFfaW50ZXJhY3Rpb25zX211dEluZm9fY29uZENvbXAuUkRTIikpCn0KYGBgCgpTYXZlIG11dHVhbCBpbmZvcm1hdGlvbiBmb3IgTFIgYW5kIGNlbGwgdHlwZXMKCmBgYHtyfQpmb3IobiBpbiBuYW1lcyhoZXJfYWxsaW50X2wpKXsKICBoZXJfYWxsaW50ID0gaGVyX2FsbGludF9sW1tuXV0KICAjIHNlbGVjdCBnZW5lcyBmcm9tIGxvd2VzdCBtdXR1YWwgKHRhYmxlIC0gZ2V0IG1vc3QgY29tbW9uKQogICMgKwogICMgbWVhbiBtdXR1YWwgcGVyIGNlbGwgdHlwZSAobG93ZXN0ID0gbW9yZSBjaGFuZ2UpCiAgIyMgcGxvdCBpbnRlcmFjdGlvbnMgYmFzZWQgb24gdGhvc2UgZ2VuZXMgKHNvbWUgYXJlIGludm9sdmVkIGluIG1vcmUgdGhhbiBvbmUpCiAgIyMgaGVhdG1hcCAtIHJvd3MgY3Q7IGNvbHVtbnMgZ2VuZXM7IGdhcHMgYmV0d2VlbiBpbnRlcmFjdDsgMSBoZWF0bWFwL2NvbmQsIHNhbWUgY3Qgb3JkZXJpbmcKICBzdWJfZGYxID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaGU+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImN0MSIsIm11dEluZm9faGUiKV0pCiAgc3ViX2RmMiA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hlPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDIiLCJtdXRJbmZvX2hlIildKQogIGN0X211dF9oZSA9IHRhcHBseShjKHN1Yl9kZjEkbXV0SW5mb19oZSwgc3ViX2RmMiRtdXRJbmZvX2hlKSwgCiAgICAgICAgICAgICAgICAgICAgIGMoc3ViX2RmMSRjdDEsIHN1Yl9kZjIkY3QyKSwgbWVhbikKICBzdWJfZGYxID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaHI+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImN0MSIsIm11dEluZm9faHIiKV0pCiAgc3ViX2RmMiA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hyPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDIiLCJtdXRJbmZvX2hyIildKQogIGN0X211dF9ociA9IHRhcHBseShjKHN1Yl9kZjEkbXV0SW5mb19ociwgc3ViX2RmMiRtdXRJbmZvX2hyKSwgCiAgICAgICAgICAgICAgICAgICAgIGMoc3ViX2RmMSRjdDEsIHN1Yl9kZjIkY3QyKSwgbWVhbikKICBzdWJfZGYxID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfZXI+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImN0MSIsIm11dEluZm9fZXIiKV0pCiAgc3ViX2RmMiA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2VyPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDIiLCJtdXRJbmZvX2VyIildKQogIGN0X211dF9lciA9IHRhcHBseShjKHN1Yl9kZjEkbXV0SW5mb19lciwgc3ViX2RmMiRtdXRJbmZvX2VyKSwgCiAgICAgICAgICAgICAgICAgICAgIGMoc3ViX2RmMSRjdDEsIHN1Yl9kZjIkY3QyKSwgbWVhbikKICBjdF9tdXRfZGYgPSBjYmluZChjdF9tdXRfaGUsY3RfbXV0X2hyLGN0X211dF9lcikKICByb3duYW1lcyhjdF9tdXRfZGYpID0gbmFtZXMoY3RfbXV0X2hlKQogIGNvbG5hbWVzKGN0X211dF9kZikgPSBjKCJoZSIsICJociIsICJlciIpCiAgc2F2ZVJEUyhjdF9tdXRfZGYsIGZpbGUgPSAiLi9yZXN1bHRzL2NlbGxfY29tbS91cGR0L2N0X3NlbGVjdF9tdXRJbmZvX2NvbmRDb21wLlJEUyIpCiAgCiAgc3ViX2RmMSA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hlPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJscjEiLCJtdXRJbmZvX2hlIildKQogIHN1Yl9kZjIgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9oZT4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIyIiwibXV0SW5mb19oZSIpXSkKICBscl9tdXRfaGUgPSB0YXBwbHkoYyhzdWJfZGYxJG11dEluZm9faGUsIHN1Yl9kZjIkbXV0SW5mb19oZSksIAogICAgICAgICAgICAgICAgICAgICBjKHN1Yl9kZjEkbHIxLCBzdWJfZGYyJGxyMiksIG1lYW4pCiAgc3ViX2RmMSA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hyPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJscjEiLCJtdXRJbmZvX2hyIildKQogIHN1Yl9kZjIgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9ocj4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIyIiwibXV0SW5mb19ociIpXSkKICBscl9tdXRfaHIgPSB0YXBwbHkoYyhzdWJfZGYxJG11dEluZm9faHIsIHN1Yl9kZjIkbXV0SW5mb19ociksIAogICAgICAgICAgICAgICAgICAgICBjKHN1Yl9kZjEkbHIxLCBzdWJfZGYyJGxyMiksIG1lYW4pCiAgc3ViX2RmMSA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2VyPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJscjEiLCJtdXRJbmZvX2VyIildKQogIHN1Yl9kZjIgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9lcj4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIyIiwibXV0SW5mb19lciIpXSkKICBscl9tdXRfZXIgPSB0YXBwbHkoYyhzdWJfZGYxJG11dEluZm9fZXIsIHN1Yl9kZjIkbXV0SW5mb19lciksIAogICAgICAgICAgICAgICAgICAgICBjKHN1Yl9kZjEkbHIxLCBzdWJfZGYyJGxyMiksIG1lYW4pCiAgCiAgbHJfY250X2hlID0gdGFibGUoYyhoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hlPjAgJiBoZXJfYWxsaW50JG11dEluZm9faGU8PTAuMDUsImxyMSJdLAogICAgICAgICAgICAgICAgICAgICAgaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9oZT4wICYgaGVyX2FsbGludCRtdXRJbmZvX2hlPD0wLjA1LCJscjIiXSkpCiAgbHJfaGUgPSBtZXJnZShscl9jbnRfaGUsIGxyX211dF9oZSwgYnkueCA9IDEsIGJ5LnkgPSAwKQogIGxyX2NudF9ociA9IHRhYmxlKGMoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9ocj4wICYgaGVyX2FsbGludCRtdXRJbmZvX2hyPD0wLjA1LCJscjEiXSwKICAgICAgICAgICAgICAgICAgICAgIGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaHI+MCAmIGhlcl9hbGxpbnQkbXV0SW5mb19ocjw9MC4wNSwibHIyIl0pKQogIGxyX2hyID0gbWVyZ2UobHJfY250X2hyLCBscl9tdXRfaHIsIGJ5LnggPSAxLCBieS55ID0gMCkKICBscl9jbnRfZXIgPSB0YWJsZShjKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfZXI+MCAmIGhlcl9hbGxpbnQkbXV0SW5mb19lcjw9MC4wNSwibHIxIl0sCiAgICAgICAgICAgICAgICAgICAgICBoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2VyPjAgJiBoZXJfYWxsaW50JG11dEluZm9fZXI8PTAuMDUsImxyMiJdKSkKICBscl9lciA9IG1lcmdlKGxyX2NudF9lciwgbHJfbXV0X2VyLCBieS54ID0gMSwgYnkueSA9IDApCiAgCiAgIyBFQ00gLSB3aGljaCBwcm90ZWlucy9jb2xsYWdlbnM/OyBtZW50aW9uIFRHRkIKICAjIGRldiAtIHdoaWNoIGxpZ2FuZHMvcmVjZXB0b3JzCiAgbHJfYWxsID0gcmJpbmQobHJfaGUsIGxyX2hyLCBscl9lcikKICBscl9hbGwkY29uZCA9IGMocmVwKCJoZSIsIG5yb3cobHJfaGUpKSwgcmVwKCJociIsIG5yb3cobHJfaHIpKSxyZXAoImVyIiwgbnJvdyhscl9lcikpKQogIGNvbG5hbWVzKGxyX2FsbCkgPSBjKCJsciIsICJuX211dC4wNSIsICJtZWFuX211dEluZm8iLCAiY29uZCIpCiAgc2F2ZVJEUyhscl9hbGwsIGZpbGUgPSBwYXN0ZTAoIi4vcmVzdWx0cy9jZWxsX2NvbW0vdXBkdC8iLCBuLCAiTFJfc2VsZWN0X211dEluZm9fY29uZENvbXAuUkRTIikpCn0KYGBgCgpQbG90IGludGVyYWN0aW9ucwoKYGBge3J9CmZvcihuIGluIG5hbWVzKGN0X2ludF9leHBfbCkpewogIGN0X2ludF9leHAgPSBjdF9pbnRfZXhwX2xbW25dXQogIHIgPSBpZihuPT0iYWxsIikgMTozIGVsc2UgNDo2CiAgCiAgIyBpbnRlcmFjdGlvbnMKICBpbnRkZiA9IHVuaXF1ZShSZWR1Y2UocmJpbmQsIHJlZm9ybV9saXN0W3JdKVssYygxLDQsNSldKQogIGludGRmJGludHBhaXIgPSBwYXN0ZTAoaW50ZGYkbHIxLCAiIC0gIiwgaW50ZGYkbHIyKQogIAogIGN0X2ludF9leHBfZmlsZSA9IHVuaXF1ZShjdF9pbnRfZXhwWyxjKDEsNDo1LDc6MTApXSkKICBjdF9pbnRfZXhwX2ZpbGUkY3RwYWlyID0gcGFzdGUwKGN0X2ludF9leHBfZmlsZSRjdCwgIiAtICIsIGN0X2ludF9leHBfZmlsZSRjdF90YXJnZXQpCiAgCiAgcGxvdF9kZl9pbnQgPSByZXNoYXBlMjo6bWVsdChjdF9pbnRfZXhwX2ZpbGVbLGMoMSw4LDQ6NyldKQogIHBsb3RfZGZfaW50JHZhcmlhYmxlID0gdW5saXN0KGxhcHBseShzdHJzcGxpdChhcy5jaGFyYWN0ZXIocGxvdF9kZl9pbnQkdmFyaWFibGUpLCAiXyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgeFtbMV1dWzFdKSkKICBwbG90X2RmX2ludCR2YXJpYWJsZSA9IGZhY3RvcihwbG90X2RmX2ludCR2YXJpYWJsZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcmV2KGMoImhlYWx0aHkiLCAiZW1ib2xpc2VkIiwgInJlZ2VuZXJhdGluZyIpKSkKICAKICBzdWJfcGxvdF9kZl9pbnQgPSBwbG90X2RmX2ludFtncmVwbCgiU3RlbGxhdGUiLCBwbG90X2RmX2ludCRjdHBhaXIpIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJLdXBmZmVyIiwgcGxvdF9kZl9pbnQkY3RwYWlyKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiTFNFQyIsIHBsb3RfZGZfaW50JGN0cGFpciksXQogIAogIHN1Yl9wbG90X2RmX2ludCA9IG1lcmdlKHN1Yl9wbG90X2RmX2ludCwgaW50ZGZbLGMoMSw0KV0sIGJ5ID0gMSwgYWxsLnggPSBUKQogIAogIGdnID0gIkVDTSIKICBwbHQgPSBnZ3Bsb3Qoc3ViX3Bsb3RfZGZfaW50W3N1Yl9wbG90X2RmX2ludCRkZXNjcmlwdGlvbj09Z2csXSwgCiAgICAgICAgIGFlcyh4ID0gY3RwYWlyLCB5ID0gdmFyaWFibGUsIGNvbG91ciA9IHZhbHVlLCBzaXplID0gdmFsdWUpKSsKICAgIGZhY2V0X2dyaWQoaW50cGFpcn4uKSsKICAgIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gImV4cCIsIHJldmVyc2UgPSBUKSwgCiAgICAgICAgICAgY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gImV4cCIsIHJldmVyc2UgPSBUKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBsYWJzKHRpdGxlID0gZ2cpKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCB2anVzdCA9IDEpLAogICAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgc2l6ZSA9IDgpKQogIHByaW50KHBsdCkKfQpgYGAKCkltcG9ydGFudCB0YWJsZXMKCmBgYHtyfQpmb3IobiBpbiBuYW1lcyhpbnRlcl9kZikpewogIHdyaXRlLmNzdihpbnRlcl9kZltbbl1dLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYsCiAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvdGFibGVzL0ludGVyYWN0XyIsIG4sICJfY2VsbHR5cGVzX2NvbmQuY3N2IikpCn0KZm9yKG4gaW4gbmFtZXMoY3RfaW50X2V4cF9sKSl7CiAgY3RfaW50X2V4cCA9IGN0X2ludF9leHBfbFtbbl1dCiAgd3JpdGUuY3N2KGN0X2ludF9leHAsIGNvbC5uYW1lcyA9IFQsIHJvdy5uYW1lcyA9IEYsIHF1b3RlID0gRiwgCiAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvdGFibGVzLyIsIG4sICJpbnRlcmFjdF9jZWxsdHlwZV9leHBfZ3JvdXAuY3N2IikpCn0KYGBgCgoKIyMgQ2VsbC1jZWxsIGNvbW11bmljYXRpb24gbmV0d29ya3MKTG9hZCBkYXRhIHRvIG1ha2UgY2VsbCBjb21tIG5ldHdvcmtzIChOT1QgVVNFRCBIRVJFKQoKYGBge3J9CnJlZG9uZV9tZXRhID0gbGlzdCgpCmZvcihjYyBpbiB1bmlxdWUoYWxsY2VsbHNfY3NzJENvbmRpdGlvbikpewogIHJlZG9uZV9tZXRhW1tjY11dID0gcmVhZC50YWJsZShwYXN0ZTAoInJlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNjLCAiLyIsIGNjLCAiX21ldGFfbmFtZXMudHh0IiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxKQp9CnJlZG9uZV9tZXRhX2FsbCA9IFJlZHVjZShyYmluZCwgcmVkb25lX21ldGEpCgphbGxjZWxsc19yZWRvbmUgPSBBZGRNZXRhRGF0YShhbGxjZWxsc19jc3MsIHJlZG9uZV9tZXRhX2FsbCkKYWxsY2VsbHNfcmVkb25lID0gYWxsY2VsbHNfcmVkb25lWywhaXMubmEoYWxsY2VsbHNfcmVkb25lJGNlbGxfdHlwZSldCmBgYAoKRnVuY3Rpb25zIHVzZWQgdG8gbWFrZSBjZWxsIGNvbW0gbmV0d29ya3MKCmBgYHtyfQptYWtlTWVkaWFuID0gZnVuY3Rpb24ocG9pbnRfZGYsIGVkZ2VfZGYsIGNsID0gYygiY3QyIiwgIm1hal9nMSIsICJtYWpfZzIiKSl7CiAgbWVhbl9tYWpvciA9IGRhdGEuZnJhbWUoIlgxIiA9IHRhcHBseShwb2ludF9kZiRYMSwgcG9pbnRfZGZbLGNsWzFdXSwgbWVkaWFuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAiWDIiID0gdGFwcGx5KHBvaW50X2RmJFgyLCBwb2ludF9kZlssY2xbMV1dLCBtZWRpYW4pKQogIG1lYW5fbWFqb3JbLGNsWzFdXSA9IHJvd25hbWVzKG1lYW5fbWFqb3IpCiAgCiAgZWRnZV9kZlssY2xbMl1dID0gZmFjdG9yKGVkZ2VfZGZbLGNsWzJdXSwgbGV2ZWxzID0gdW5pcXVlKGMoZWRnZV9kZlssY2xbMl1dLCBlZGdlX2RmWyxjbFszXV0pKSkKICBlZGdlX2RmWyxjbFszXV0gPSBmYWN0b3IoZWRnZV9kZlssY2xbM11dLCBsZXZlbHMgPSB1bmlxdWUoYyhhcy5jaGFyYWN0ZXIoZWRnZV9kZlssY2xbMl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX2RmWyxjbFszXV0pKSkKICAKICBtYWpfbWF0ID0gdGFibGUoZWRnZV9kZlssY2xbMl1dLCBlZGdlX2RmWyxjbFszXV0pCiAgZGlhZyhtYWpfbWF0KSA9IDAKICBlZGdlX21ham9yID0gZGF0YS5mcmFtZShtYWpfbWF0ICsgdChtYWpfbWF0KSkKICBlZGdlX21ham9yID0gbWVyZ2UoZWRnZV9tYWpvciwgbWVhbl9tYWpvciwgYnkueCA9IDEsIGJ5LnkgPSAzKQogIGVkZ2VfbWFqb3IgPSBtZXJnZShlZGdlX21ham9yLCBtZWFuX21ham9yLCBieS54ID0gMiwgYnkueSA9IDMpCiAgCiAgY2xjb21iID0gY29tYm4odW5pcXVlKGMoYXMuY2hhcmFjdGVyKGVkZ2VfbWFqb3IkVmFyMSksIGFzLmNoYXJhY3RlcihlZGdlX21ham9yJFZhcjIpKSksIDIpCiAga2VlcCA9IGMoKQogIGZvcihqIGluIDE6bmNvbChjbGNvbWIpKXsKICAgIGtlZXAgPSBjKGtlZXAsIHdoaWNoKGVkZ2VfbWFqb3IkVmFyMj09Y2xjb21iWzEsal0gJiBlZGdlX21ham9yJFZhcjE9PWNsY29tYlsyLGpdKSkKICB9CiAgZWRnZV9tYWpvciA9IGVkZ2VfbWFqb3Jba2VlcCxdCiAgCiAgcmV0dXJuKGxpc3QobWVhbl9tYWpvciA9IG1lYW5fbWFqb3IsIGVkZ2VfbWFqb3IgPSBlZGdlX21ham9yKSkKfQoKbWFrZU1lZGlhbkNvbmQgPSBmdW5jdGlvbihwb2ludF9kZiwgZWRnZV9kZiwgY2wgPSBjKCJjdCIsICJjdF9nMSIsICJjdF9nMiIpLCBlZGdlX2J5ID0gImNvbmRpdGlvbiIpewogIG1lYW5fbWFqb3IgPSBkYXRhLmZyYW1lKCJYMSIgPSB0YXBwbHkocG9pbnRfZGYkWDEsIHBvaW50X2RmWyxjbFsxXV0sIG1lZGlhbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlgyIiA9IHRhcHBseShwb2ludF9kZiRYMiwgcG9pbnRfZGZbLGNsWzFdXSwgbWVkaWFuKSkKICBtZWFuX21ham9yWyxjbFsxXV0gPSByb3duYW1lcyhtZWFuX21ham9yKQogIAogIGVkZ2VfZGZbLGNsWzJdXSA9IGZhY3RvcihlZGdlX2RmWyxjbFsyXV0sIGxldmVscyA9IHVuaXF1ZShjKGVkZ2VfZGZbLGNsWzJdXSwgZWRnZV9kZlssY2xbM11dKSkpCiAgZWRnZV9kZlssY2xbM11dID0gZmFjdG9yKGVkZ2VfZGZbLGNsWzNdXSwgbGV2ZWxzID0gdW5pcXVlKGMoYXMuY2hhcmFjdGVyKGVkZ2VfZGZbLGNsWzJdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWRnZV9kZlssY2xbM11dKSkpCiAgCiAgZWRnZV9sID0gbGlzdCgpCiAgZm9yKGkgaW4gdW5pcXVlKGVkZ2VfZGZbLGVkZ2VfYnldKSl7CiAgICBzdWJfZWRnZV9kZiA9IGVkZ2VfZGZbZWRnZV9kZlssZWRnZV9ieV09PWksXQogICAgbWFqX21hdCA9IHRhYmxlKHN1Yl9lZGdlX2RmWyxjbFsyXV0sIHN1Yl9lZGdlX2RmWyxjbFszXV0pCiAgICBkaWFnKG1hal9tYXQpID0gMAogICAgZWRnZV9tYWpvciA9IGRhdGEuZnJhbWUobWFqX21hdCArIHQobWFqX21hdCkpCiAgICBlZGdlX21ham9yID0gbWVyZ2UoZWRnZV9tYWpvciwgbWVhbl9tYWpvciwgYnkueCA9IDEsIGJ5LnkgPSAzKQogICAgZWRnZV9tYWpvciA9IG1lcmdlKGVkZ2VfbWFqb3IsIG1lYW5fbWFqb3IsIGJ5LnggPSAyLCBieS55ID0gMykKICAgIAogICAgIyByZW1vdmUgcmVwZWF0ZWQKICAgIGNsY29tYiA9IGNvbWJuKHVuaXF1ZShjKGFzLmNoYXJhY3RlcihlZGdlX21ham9yJFZhcjEpLCBhcy5jaGFyYWN0ZXIoZWRnZV9tYWpvciRWYXIyKSkpLCAyKQogICAga2VlcCA9IGMoKQogICAgZm9yKGogaW4gMTpuY29sKGNsY29tYikpewogICAgICBrZWVwID0gYyhrZWVwLCB3aGljaChlZGdlX21ham9yJFZhcjI9PWNsY29tYlsxLGpdICYgZWRnZV9tYWpvciRWYXIxPT1jbGNvbWJbMixqXSkpCiAgICB9CiAgICAKICAgIGVkZ2VfbFtbaV1dID0gZWRnZV9tYWpvcltrZWVwLF0KICB9CiAgZWRnZV9tYWpvciA9IFJlZHVjZShyYmluZCwgZWRnZV9sKQogIGVkZ2VfbWFqb3JbLGVkZ2VfYnldID0gdW5saXN0KGxhcHBseShuYW1lcyhlZGdlX2wpLCBmdW5jdGlvbih4KSByZXAoeCwgbnJvdyhlZGdlX2xbW3hdXSkpKSkKICBlZGdlX21ham9yID0gZWRnZV9tYWpvcltlZGdlX21ham9yJFZhcjIhPWVkZ2VfbWFqb3IkVmFyMSxdCiAgCiAgcmV0dXJuKGxpc3QobWVhbl9tYWpvciA9IG1lYW5fbWFqb3IsIGVkZ2VfbWFqb3IgPSBlZGdlX21ham9yKSkKfQpgYGAKClBsb3QgbGlnYW5kcyBhbmQgcmVjZXB0b3JzIHdpdGggTURTCgpgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTQuNn0KaW50ZXJfZGYgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9jb25kX2RpZmZfaW50ZXJhY3RfREUuUkRTIikKCiMgaW50ZXJhY3Rpb25zIHVuaXF1ZSB0byBlYWNoIGNvbmRpdGlvbgp1bmlxdWVfaW50ZXJzID0gYyhzZXRkaWZmKGludGVyX2RmJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGMoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLCBpbnRlcl9kZiRyZWdlbmVyYXRpbmckaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICAgc2V0ZGlmZihpbnRlcl9kZiRlbWJvbGlzZWQkaWRfY3BfaW50ZXJhY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGMoaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbiwgaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgIHNldGRpZmYoaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBjKGludGVyX2RmJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbiwgaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbikpKQoKIyBpbnRlcmFjdGlvbnMgdW5pcXVlIHRvIGhlYWx0aHkgb3IgdG8gZW1iL3JlZ2VuCmNvbXBoX2ludGVycyA9IGMoc2V0ZGlmZihpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBjKGludGVyX2RmJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbiwgaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgc2V0ZGlmZihpbnRlcl9kZiRlbWJvbGlzZWQkaWRfY3BfaW50ZXJhY3Rpb24sIGludGVyX2RmJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24pLAogICAgICAgICAgICAgICAgIHNldGRpZmYoaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uLCBpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uKSkKCiMgcHJlcGFyZSBnZW5lIHBhaXJzIHBlciBjb25kaXRpb24KZ2VuZV9wYWlyc19jb25kID0gcmJpbmQodW5pcXVlKGludGVyX2RmJGhlYWx0aHlbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgImduMSIsICJnbjIiKV0pLAogICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkZW1ib2xpc2VkWyxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsICJnbjEiLCAiZ24yIildKSwKICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGludGVyX2RmJHJlZ2VuZXJhdGluZ1ssYygiaWRfY3BfaW50ZXJhY3Rpb24iLCAiZ24xIiwgImduMiIpXSkpCmdlbmVfcGFpcnNfY29uZCRjb25kaXRpb24gPSBjKHJlcCgiaGVhbHRoeSIsIG5yb3codW5pcXVlKGludGVyX2RmJGhlYWx0aHlbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImduMSIsICJnbjIiKV0pKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiZW1ib2xpc2VkIiwgbnJvdyh1bmlxdWUoaW50ZXJfZGYkZW1ib2xpc2VkWyxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ24xIiwgImduMiIpXSkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJyZWdlbmVyYXRpbmciLCBucm93KHVuaXF1ZShpbnRlcl9kZiRyZWdlbmVyYXRpbmdbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImduMSIsICJnbjIiKV0pKSkpCgojIGxpc3QgYWxsIExSIGdlbmVzCmFsbF9scl9nZW5lcyA9IHVuaXF1ZShjKGFzLmNoYXJhY3RlcihpbnRlcl9kZiRoZWFsdGh5JGduMSksIGFzLmNoYXJhY3RlcihpbnRlcl9kZiRoZWFsdGh5JGduMiksCiAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihpbnRlcl9kZiRlbWJvbGlzZWQkZ24xKSwgYXMuY2hhcmFjdGVyKGludGVyX2RmJGVtYm9saXNlZCRnbjIpLAogICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGduMSksIGFzLmNoYXJhY3RlcihpbnRlcl9kZiRyZWdlbmVyYXRpbmckZ24yKSkpCmFsbF9scl9nZW5lcyA9IGFsbF9scl9nZW5lc1thbGxfbHJfZ2VuZXMgJWluJSByb3duYW1lcyhzdWJfYWxsY2VsbHNfY3NzQGFzc2F5cyRTQ1RAZGF0YSldCgojIGNhbGN1bGF0ZSBtZWFuIHBlciBjZWxsIHR5cGUgYW5kIGNvbmRpdGlvbiBmb3IgZWFjaCBMUiBnZW5lCm1lYW5fZXhwX2NvbmRfbHIgPSBhcHBseShzdWJfYWxsY2VsbHNfY3NzQGFzc2F5cyRTQ1RAZGF0YVthbGxfbHJfZ2VuZXMsXSwgMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB0YXBwbHkoeCwgcGFzdGUwKHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJfIiwgc3ViX2FsbGNlbGxzX2NzcyRDb25kaXRpb24pLCBtZWFuKSkKCiMgZGV0ZXJtaW5lIHRoZSBjZWxsIHR5cGUgYW5kIGNvbmRpdGlvbiB3aXRoIHRoZSBoaWdoZXN0IGV4cHJlc3Npb24KbWF4X2NvbmRfY3QgPSByb3duYW1lcyhtZWFuX2V4cF9jb25kX2xyKVthcHBseShtZWFuX2V4cF9jb25kX2xyLCAyLCB3aGljaC5tYXgpXQpuYW1lcyhtYXhfY29uZF9jdCkgPSBjb2xuYW1lcyhtZWFuX2V4cF9jb25kX2xyKQptYXhfY29uZF9jdF9jdCA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQobWF4X2NvbmRfY3QsICJfIiksIGZ1bmN0aW9uKHgpIHhbMV0pKQptYXhfY29uZF9jdF9jb25kID0gdW5saXN0KGxhcHBseShzdHJzcGxpdChtYXhfY29uZF9jdCwgIl8iKSwgZnVuY3Rpb24oeCkgeFsyXSkpCgojIGNvcnJlbGF0aW9uIG9mIG1lYW4gZXhwcmVzc2lvbgpjb3JfY29uZF9sciA9IGNvcihtZWFuX2V4cF9jb25kX2xyLCBtZXRob2QgPSAic3AiKQoKIyBmaWx0ZXIgY29ycmVsYXRpb24gd2l0aCBpdHNlbGYsIGtlZXAgb25seSBnZW5lcyB3aXRoIGNvcj49MC4zCmRpYWcoY29yX2NvbmRfbHIpID0gMAphZGpfY29uZF9tYXQgPSBjb3JfY29uZF9scj49MC4zCgojIGJ1aWxkIGdyYXBoLCBwcm9qZWN0IHdpdGggTURTCm5ldHdvcmtfY29uZCA9IGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeChhZGpfY29uZF9tYXQsIHdlaWdodGVkPVQsIG1vZGU9InVuZGlyZWN0ZWQiLCBkaWFnPUYpCmxfY29uZCA9IGlncmFwaDo6bGF5b3V0X3dpdGhfbWRzKG5ldHdvcmtfY29uZCkKbF9jb25kID0gZGF0YS5mcmFtZShsX2NvbmQpCmxfY29uZCRnZW5lID0gY29sbmFtZXMoYWRqX2NvbmRfbWF0KQpyb3duYW1lcyhsX2NvbmQpID0gY29sbmFtZXMoYWRqX2NvbmRfbWF0KQoKIyBkZWZpbmUgYWxsIGVkZ2VzLCBiYXNlZCBvbiBDZWxsUGhvbmVEQiBwYWlyaW5ncwp0bXBfZGYgPSBtZXJnZShnZW5lX3BhaXJzX2NvbmQsIGxfY29uZCwgYnkueCA9ICJnbjEiLCBieS55ID0gImdlbmUiKQplZGdlX2NvbmRfZGYgPSBtZXJnZSh0bXBfZGYsIGxfY29uZCwgYnkueCA9ICJnbjIiLCBieS55ID0gImdlbmUiKQplZGdlX2NvbmRfZGYgPSBtZXJnZShlZGdlX2NvbmRfZGYsIGRhdGEuZnJhbWUobWF4X2NvbmRfY3RfY3QpLCBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKZWRnZV9jb25kX2RmID0gbWVyZ2UoZWRnZV9jb25kX2RmLCBkYXRhLmZyYW1lKG1heF9jb25kX2N0X2N0KSwgYnkueCA9IDIsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmNvbG5hbWVzKGVkZ2VfY29uZF9kZilbOToxMF0gPSBjKCJjdF9nMSIsICJjdF9nMiIpCiMgYWRkIGhpZ2hlc3QgZXhwcmVzc2luZyBtYWpvciBjZWxsIHR5cGVzCmVkZ2VfY29uZF9kZiRtYWpfZzEgPSBpZmVsc2UoZ3JlcGwoIkxTRUMiLCBlZGdlX2NvbmRfZGYkY3RfZzEpLCAiRW5kb3RoZWxpYWwiLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJIZXBhdG9jeXRlcyIsIGVkZ2VfY29uZF9kZiRjdF9nMSksICJIZXBhdG9jeXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJTdGVsbGF0ZSBjZWxscyIsIGVkZ2VfY29uZF9kZiRjdF9nMSksICJNZXNlbmNoeW1hbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiQ2hvbGFuZ2lvY3l0ZXMiLCBlZGdlX2NvbmRfZGYkY3RfZzEpLCAiQ2hvbGFuZ2lvY3l0ZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJbW11bmUiKSkpKQplZGdlX2NvbmRfZGYkbWFqX2cyID0gaWZlbHNlKGdyZXBsKCJMU0VDIiwgZWRnZV9jb25kX2RmJGN0X2cyKSwgIkVuZG90aGVsaWFsIiwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiSGVwYXRvY3l0ZXMiLCBlZGdlX2NvbmRfZGYkY3RfZzIpLCAiSGVwYXRvY3l0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiU3RlbGxhdGUgY2VsbHMiLCBlZGdlX2NvbmRfZGYkY3RfZzIpLCAiTWVzZW5jaHltYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkNob2xhbmdpb2N5dGVzIiwgZWRnZV9jb25kX2RmJGN0X2cyKSwgIkNob2xhbmdpb2N5dGVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSW1tdW5lIikpKSkKZWRnZV9jb25kX2RmID0gbWVyZ2UoZWRnZV9jb25kX2RmLCBkYXRhLmZyYW1lKG1heF9jb25kX2N0X2NvbmQpLCBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKZWRnZV9jb25kX2RmID0gbWVyZ2UoZWRnZV9jb25kX2RmLCBkYXRhLmZyYW1lKG1heF9jb25kX2N0X2NvbmQpLCBieS54ID0gMiwgYnkueSA9IDAsIGFsbC54ID0gVCkKCiMgZGVmaW5lIHRoZSB2ZXJ0aWNlcyBvZiB0aGUgbmV0d29yawpwb2ludF9jb25kX2RmID0gbF9jb25kCnBvaW50X2NvbmRfZGYkY3QgPSBtYXhfY29uZF9jdF9jdFtwb2ludF9jb25kX2RmJGdlbmVdCnBvaW50X2NvbmRfZGYkY3QyID0gaWZlbHNlKGdyZXBsKCJMU0VDIiwgcG9pbnRfY29uZF9kZiRjdCksICJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkhlcGF0b2N5dGVzIiwgcG9pbnRfY29uZF9kZiRjdCksICJIZXBhdG9jeXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJTdGVsbGF0ZSBjZWxscyIsIHBvaW50X2NvbmRfZGYkY3QpLCAiTWVzZW5jaHltYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkNob2xhbmdpb2N5dGVzIiwgcG9pbnRfY29uZF9kZiRjdCksICJDaG9sYW5naW9jeXRlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkltbXVuZSIpKSkpCnBvaW50X2NvbmRfZGYkY29uZCA9IG1heF9jb25kX2N0X2NvbmRbcG9pbnRfY29uZF9kZiRnZW5lXQoKIyBkZWZpbmUgdGhlIG1lZGlhbiBwb2ludHMgZm9yIGVhY2ggY2VsbCB0eXBlICh1c2luZyBtYXggZXhwcmVzc2lvbikKcGVfbCA9IG1ha2VNZWRpYW4ocG9pbnRfY29uZF9kZiwgZWRnZV9jb25kX2RmLCBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgojIHBsb3QgdG90YWwgZ2VuZSBjb3JyZWxhdGlvbiBwcm9qZWN0aW9uIGFuZCBtZWRpYW4gbmV0d29yawpwbHRib3RoID0gZ2dwbG90KCkrCiAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfY29uZF9kZiwgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3VyID0gY3QyKSwgCiAgICAgICAgICAgICBhbHBoYSA9IDAuMjUsIHNob3cubGVnZW5kID0gRikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMCw0LDE5KSkrCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBwZV9sW1syXV0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55LCBzaXplID0gRnJlcSksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMTUsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV9wb2ludChkYXRhID0gcGVfbFtbMV1dLCAKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX2xbWzJdXSRGcmVxKSkrCiAgdGhlbWVfY2xhc3NpYygpKyB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpwcmludChwbHRib3RoKQoKcGx0Ym90aCA9IGdncGxvdCgpKwogIGdlb21fc2VnbWVudChkYXRhID0gZWRnZV9jb25kX2RmLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMDMsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfY29uZF9kZiwgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3VyID0gY3QyKSwgCiAgICAgICAgICAgICBhbHBoYSA9IDAuNiwgc2hvdy5sZWdlbmQgPSBGKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygwLDQsMTkpKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwZV9sW1sxXV0sIAogICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfbFtbMl1dJEZyZXEpKSsKICB0aGVtZV9jbGFzc2ljKCkrIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCnByaW50KHBsdGJvdGgpCgojIGdldCBtZWRpYW4gcGVyIGNlbGwgdHlwZSwgcGVyIGNvbmRpdGlvbiAtIEZVTEwgTkVUV09SSwpwZV9jb25kX2wgPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX2RmLCBlZGdlX2NvbmRfZGYsIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSkKCnBsdF9jb25kX2wgPSBsaXN0KCkKZm9yKGNjIGluIHVuaXF1ZShwZV9jb25kX2xbWzJdXSRjb25kaXRpb24pKXsKICBwbHRfY29uZF9sW1tjY11dID0gZ2dwbG90KCkrCiAgICBnZW9tX3NlZ21lbnQoZGF0YSA9IHBlX2NvbmRfbFtbMl1dW3BlX2NvbmRfbFtbMl1dJGNvbmRpdGlvbj09Y2MsXSwgCiAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gRnJlcSwgYWxwaGEgPSBGcmVxKSkrCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwZV9jb25kX2xbWzFdXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9jb25kX2xbWzJdXSRGcmVxKSkrCiAgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKHBlX2NvbmRfbFtbMl1dJEZyZXEpKSsKICAgIGxhYnModGl0bGUgPSBjYykrCiAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSwKICAgICAgICAgICBhbHBoYSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSkrCiAgICB0aGVtZV9jbGFzc2ljKCkKfQpwbHRfY29uZF9sW1sibGVnIl1dID0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfY29uZF9sW1siaGVhbHRoeSJdXSkKCiMgcGxvdCBGVUxMIE5FVFdPUksgbWVkaWFuIHBlciBjb25kaXRpb24KY293cGxvdDo6cGxvdF9ncmlkKHBsdF9jb25kX2xbWzFdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCAKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xbWzJdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgcGx0X2NvbmRfbFtbM11dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIHBsdF9jb25kX2wkbGVnLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSA0LCByZWxfd2lkdGhzID0gYygxLDEsMSwwLjUpKQoKIyBnZXQgbWVkaWFuIHBlciBjZWxsIHR5cGUsIHBlciBjb25kaXRpb24gLSBVTklRVUUgUEVSIENPTkRUSU9OIE5FVFdPUksKcGVfY29uZF9sX3UgPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX2RmLCBlZGdlX2NvbmRfZGZbZWRnZV9jb25kX2RmJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgdW5pcXVlX2ludGVycyxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSkKCnBsdF9jb25kX2xfdSA9IGxpc3QoKQpmb3IoY2MgaW4gdW5pcXVlKHBlX2NvbmRfbFtbMl1dJGNvbmRpdGlvbikpewogIHBsdF9jb25kX2xfdVtbY2NdXSA9IGdncGxvdCgpKwogICAgZ2VvbV9zZWdtZW50KGRhdGEgPSBwZV9jb25kX2xfdVtbMl1dW3BlX2NvbmRfbF91W1syXV0kY29uZGl0aW9uPT1jYyxdLCAKICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55LCBzaXplID0gRnJlcSwgYWxwaGEgPSBGcmVxKSkrCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwZV9jb25kX2xfdVtbMV1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICAgIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX2NvbmRfbF91W1syXV0kRnJlcSkpKwogICAgc2NhbGVfYWxwaGFfY29udGludW91cyhsaW1pdHMgPSByYW5nZShwZV9jb25kX2xfdVtbMl1dJEZyZXEpKSsKICAgIGxhYnModGl0bGUgPSBjYykrCiAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSwKICAgICAgICAgICBhbHBoYSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSkrCiAgICB0aGVtZV9jbGFzc2ljKCkKfQpwbHRfY29uZF9sX3VbWyJsZWciXV0gPSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9jb25kX2xfdVtbImhlYWx0aHkiXV0pCgojIHBsb3QgVU5JUVVFIFBFUiBDT05EVElPTiBORVRXT1JLIG1lZGlhbiBwZXIgY29uZGl0aW9uCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbHRfY29uZF9sX3VbWzFdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCAKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xfdVtbMl1dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX3VbWzNdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCBwbHRfY29uZF9sX3UkbGVnLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSA0LCByZWxfd2lkdGhzID0gYygxLDEsMSwwLjUpKQoKIyBnZXQgbWVkaWFuIHBlciBjZWxsIHR5cGUsIHBlciBjb25kaXRpb24gLSBIRUFMVEhZIE5FVFdPUksKcGVfY29uZF9sX2ggPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX2RmLCBlZGdlX2NvbmRfZGZbZWRnZV9jb25kX2RmJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbixdLCBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgpwbHRfY29uZF9sX2ggPSBsaXN0KCkKZm9yKGNjIGluIHVuaXF1ZShwZV9jb25kX2xbWzJdXSRjb25kaXRpb24pKXsKICBwbHRfY29uZF9sX2hbW2NjXV0gPSBnZ3Bsb3QoKSsKICAgIGdlb21fc2VnbWVudChkYXRhID0gcGVfY29uZF9sX2hbWzJdXVtwZV9jb25kX2xfaFtbMl1dJGNvbmRpdGlvbj09Y2MsXSwgCiAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSwgc2l6ZSA9IEZyZXEsIGFscGhhID0gRnJlcSkpKwogICAgZ2VvbV9wb2ludChkYXRhID0gcGVfY29uZF9sX2hbWzFdXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9jb25kX2xfaFtbMl1dJEZyZXEpKSsKICAgIHNjYWxlX2FscGhhX2NvbnRpbnVvdXMobGltaXRzID0gcmFuZ2UocGVfY29uZF9sX2hbWzJdXSRGcmVxKSkrCiAgICBsYWJzKHRpdGxlID0gY2MpKwogICAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBucm93ID0gMiksCiAgICAgICAgICAgYWxwaGEgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBucm93ID0gMikpKwogICAgdGhlbWVfY2xhc3NpYygpCn0KcGx0X2NvbmRfbF9oW1sibGVnIl1dID0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfY29uZF9sX2hbWyJoZWFsdGh5Il1dKQoKIyBwbG90IEhFQUxUSFkgTkVUV09SSyBtZWRpYW4gcGVyIGNvbmRpdGlvbgpjb3dwbG90OjpwbG90X2dyaWQocGx0X2NvbmRfbF9oW1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX2hbWzJdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgcGx0X2NvbmRfbF9oW1szXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgcGx0X2NvbmRfbF9oJGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gNCwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKCiMgZ2V0IG1lZGlhbiBwZXIgY2VsbCB0eXBlLCBwZXIgY29uZGl0aW9uIC0gSEVBTFRIWSBDT01QQVJJU09OIE5FVFdPUksKcGVfY29uZF9sX2NoID0gbWFrZU1lZGlhbkNvbmQocG9pbnRfY29uZF9kZiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWRnZV9jb25kX2RmW2VkZ2VfY29uZF9kZiRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIGNvbXBoX2ludGVycyxdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgpwbHRfY29uZF9sX2NoID0gbGlzdCgpCmZvcihjYyBpbiB1bmlxdWUocGVfY29uZF9sW1syXV0kY29uZGl0aW9uKSl7CiAgcGx0X2NvbmRfbF9jaFtbY2NdXSA9IGdncGxvdCgpKwogICAgZ2VvbV9zZWdtZW50KGRhdGEgPSBwZV9jb25kX2xfY2hbWzJdXVtwZV9jb25kX2xfY2hbWzJdXSRjb25kaXRpb249PWNjLF0sIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIHNpemUgPSBGcmVxLCBhbHBoYSA9IEZyZXEpKSsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHBlX2NvbmRfbF9jaFtbMV1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICAgIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX2NvbmRfbF9jaFtbMl1dJEZyZXEpKSsKICAgIHNjYWxlX2FscGhhX2NvbnRpbnVvdXMobGltaXRzID0gcmFuZ2UocGVfY29uZF9sX2NoW1syXV0kRnJlcSkpKwogICAgbGFicyh0aXRsZSA9IGNjKSsKICAgIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpLAogICAgICAgICAgIGFscGhhID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpKSsKICAgIHRoZW1lX2NsYXNzaWMoKQp9CnBsdF9jb25kX2xfY2hbWyJsZWciXV0gPSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9jb25kX2xfY2hbWyJoZWFsdGh5Il1dKQoKIyBwbG90IEhFQUxUSFkgQ09NUEFSSVNPTiBORVRXT1JLIG1lZGlhbiBwZXIgY29uZGl0aW9uCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbHRfY29uZF9sX2NoW1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX2NoW1syXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xfY2hbWzNdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCBwbHRfY29uZF9sX2NoJGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gMiwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKYGBgCgpTYXZlIG5ldHdvcmsgb2JqZWN0cwoKYGBge3J9CnNhdmUoZWRnZV9jb25kX2RmLCBwb2ludF9jb25kX2RmLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvbmV0d29ya3NfY29uZC5SRGF0YSIpCnNhdmUocGVfbCwgcGVfY29uZF9sLCBwZV9jb25kX2xfdSwgcGVfY29uZF9sX2gsIHBlX2NvbmRfbF9jaCwgCiAgICAgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L21lZGlhbl9uZXR3b3Jrc19jb25kLlJEYXRhIikKYGBgCgpQbG90IGxpZ2FuZHMgYW5kIHJlY2VwdG9ycyB3aXRoIFVNQVAKCmBgYHtyfQpzZXQuc2VlZCgyOTU0KQpsID0gdXdvdDo6dW1hcCh0KG1lYW5fZXhwX2NvbmRfbHIpLCBtZXRyaWMgPSAiY29zaW5lIiwgcmV0X25uID0gVCwgbl9lcG9jaHMgPSAxMDAwKQpsX2NvbmQgPSBkYXRhLmZyYW1lKGwkZW1iZWRkaW5nKQpsX2NvbmQkZ2VuZSA9IGNvbG5hbWVzKG1lYW5fZXhwX2NvbmRfbHIpCnJvd25hbWVzKGxfY29uZCkgPSBjb2xuYW1lcyhtZWFuX2V4cF9jb25kX2xyKQp0bXBfZGYgPSBtZXJnZShnZW5lX3BhaXJzX2NvbmQsIGxfY29uZCwgYnkueCA9ICJnbjEiLCBieS55ID0gImdlbmUiKQplZGdlX2NvbmRfdW1hcF9kZiA9IG1lcmdlKHRtcF9kZiwgbF9jb25kLCBieS54ID0gImduMiIsIGJ5LnkgPSAiZ2VuZSIpCmVkZ2VfY29uZF91bWFwX2RmID0gbWVyZ2UoZWRnZV9jb25kX3VtYXBfZGYsIGRhdGEuZnJhbWUobWF4X2NvbmRfY3RfY3QpLCBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKZWRnZV9jb25kX3VtYXBfZGYgPSBtZXJnZShlZGdlX2NvbmRfdW1hcF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jdCksIGJ5LnggPSAyLCBieS55ID0gMCwgYWxsLnggPSBUKQpjb2xuYW1lcyhlZGdlX2NvbmRfdW1hcF9kZilbOToxMF0gPSBjKCJjdF9nMSIsICJjdF9nMiIpCmVkZ2VfY29uZF91bWFwX2RmJG1hal9nMSA9IGlmZWxzZShncmVwbCgiTFNFQyIsIGVkZ2VfY29uZF91bWFwX2RmJGN0X2cxKSwgIkVuZG90aGVsaWFsIiwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiSGVwYXRvY3l0ZXMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMSksICJIZXBhdG9jeXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJTdGVsbGF0ZSBjZWxscyIsIGVkZ2VfY29uZF91bWFwX2RmJGN0X2cxKSwgIk1lc2VuY2h5bWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJDaG9sYW5naW9jeXRlcyIsIGVkZ2VfY29uZF91bWFwX2RmJGN0X2cxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDaG9sYW5naW9jeXRlcyIsICJJbW11bmUiKSkpKQplZGdlX2NvbmRfdW1hcF9kZiRtYWpfZzIgPSBpZmVsc2UoZ3JlcGwoIkxTRUMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMiksICJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkhlcGF0b2N5dGVzIiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzIpLCAiSGVwYXRvY3l0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiU3RlbGxhdGUgY2VsbHMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMiksICJNZXNlbmNoeW1hbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiQ2hvbGFuZ2lvY3l0ZXMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hvbGFuZ2lvY3l0ZXMiLCAiSW1tdW5lIikpKSkKZWRnZV9jb25kX3VtYXBfZGYgPSBtZXJnZShlZGdlX2NvbmRfdW1hcF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jb25kKSwgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmVkZ2VfY29uZF91bWFwX2RmID0gbWVyZ2UoZWRnZV9jb25kX3VtYXBfZGYsIGRhdGEuZnJhbWUobWF4X2NvbmRfY3RfY29uZCksIGJ5LnggPSAyLCBieS55ID0gMCwgYWxsLnggPSBUKQpjb2xuYW1lcyhlZGdlX2NvbmRfdW1hcF9kZilbNF0gPSAiY29uZCIKCnBvaW50X2NvbmRfdW1hcF9kZiA9IGxfY29uZApwb2ludF9jb25kX3VtYXBfZGYkY3QgPSBtYXhfY29uZF9jdF9jdFtwb2ludF9jb25kX3VtYXBfZGYkZ2VuZV0KcG9pbnRfY29uZF91bWFwX2RmJGN0MiA9IGlmZWxzZShncmVwbCgiTFNFQyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCksICJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkhlcGF0b2N5dGVzIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KSwgIkhlcGF0b2N5dGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIlN0ZWxsYXRlIGNlbGxzIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KSwgIk1lc2VuY2h5bWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJDaG9sYW5naW9jeXRlcyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCksICJDaG9sYW5naW9jeXRlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkltbXVuZSIpKSkpCnBvaW50X2NvbmRfdW1hcF9kZiRjb25kID0gbWF4X2NvbmRfY3RfY29uZFtwb2ludF9jb25kX3VtYXBfZGYkZ2VuZV0KCgpwZV91bWFwX2wgPSBtYWtlTWVkaWFuKHBvaW50X2NvbmRfdW1hcF9kZiwgZWRnZV9jb25kX3VtYXBfZGYsIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSkKCiMgcGxvdCB0b3RhbCBnZW5lIGNvcnJlbGF0aW9uIHByb2plY3Rpb24gYW5kIG1lZGlhbiBuZXR3b3JrCnBsdGJvdGggPSBnZ3Bsb3QoKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9jb25kX3VtYXBfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG91ciA9IGN0MiksIAogICAgICAgICAgICAgYWxwaGEgPSAwLjI1LCBzaG93LmxlZ2VuZCA9IEYpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDAsNCwxOSkpKwogIGdlb21fc2VnbWVudChkYXRhID0gcGVfdW1hcF9sW1syXV0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55LCBzaXplID0gRnJlcSksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMTUsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV9wb2ludChkYXRhID0gcGVfdW1hcF9sW1sxXV0sIAogICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfbFtbMl1dJEZyZXEpKSsKICB0aGVtZV9jbGFzc2ljKCkrIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCnByaW50KHBsdGJvdGgpCgpwbHRib3RoID0gZ2dwbG90KCkrCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBlZGdlX2NvbmRfdW1hcF9kZiwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnkpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjAzLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fcG9pbnQoZGF0YSA9IHBvaW50X2NvbmRfdW1hcF9kZiwgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3VyID0gY3QyKSwgCiAgICAgICAgICAgICBhbHBoYSA9IDAuNiwgc2hvdy5sZWdlbmQgPSBGKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygwLDQsMTkpKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwZV91bWFwX2xbWzFdXSwgCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBmaWxsID0gY3QpLCAKICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9sW1syXV0kRnJlcSkpKwogIHRoZW1lX2NsYXNzaWMoKSsgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKcHJpbnQocGx0Ym90aCkKCiMgZ2V0IG1lZGlhbiBwZXIgY2VsbCB0eXBlLCBwZXIgY29uZGl0aW9uIC0gSEVBTFRIWSBDT01QQVJJU09OIE5FVFdPUksKcGVfdW1hcF9jb25kX2xfY2ggPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX3VtYXBfZGYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2VfY29uZF91bWFwX2RmW2VkZ2VfY29uZF91bWFwX2RmJGlkX2NwX2ludGVyYWN0aW9uJWluJWNvbXBoX2ludGVycyxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2VfYnkgPSAiY29uZCIsIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSkKCnBsdF91bWFwX2NvbmRfbF9jaCA9IGxpc3QoKQpmb3IoY2MgaW4gdW5pcXVlKHBlX2NvbmRfbFtbMl1dJGNvbmQpKXsKICBwbHRfdW1hcF9jb25kX2xfY2hbW2NjXV0gPSBnZ3Bsb3QoKSsKICAgIGdlb21fc2VnbWVudChkYXRhID0gcGVfdW1hcF9jb25kX2xfY2hbWzJdXVtwZV91bWFwX2NvbmRfbF9jaFtbMl1dJGNvbmQ9PWNjLF0sIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIHNpemUgPSBGcmVxLCBhbHBoYSA9IEZyZXEpKSsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHBlX3VtYXBfY29uZF9sX2NoW1sxXV0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBmaWxsID0gY3QpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfdW1hcF9jb25kX2xfY2hbWzJdXSRGcmVxKSkrCiAgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKHBlX3VtYXBfY29uZF9sX2NoW1syXV0kRnJlcSkpKwogICAgbGFicyh0aXRsZSA9IGNjKSsKICAgIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpLAogICAgICAgICAgIGFscGhhID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpKSsKICAgIHRoZW1lX2NsYXNzaWMoKQp9CnBsdF91bWFwX2NvbmRfbF9jaFtbImxlZyJdXSA9IGNvd3Bsb3Q6OmdldF9sZWdlbmQocGx0X3VtYXBfY29uZF9sX2NoW1siaGVhbHRoeSJdXSkKCiMgcGxvdCBIRUFMVEhZIENPTVBBUklTT04gTkVUV09SSyBtZWRpYW4gcGVyIGNvbmRpdGlvbgpjb3dwbG90OjpwbG90X2dyaWQocGx0X3VtYXBfY29uZF9sX2NoW1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfdW1hcF9jb25kX2xfY2hbWzJdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgcGx0X3VtYXBfY29uZF9sX2NoW1szXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgcGx0X3VtYXBfY29uZF9sX2NoJGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gNCwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKYGBgCgpTYXZlIFVNQVAgbmV0d29yayBvYmplY3RzCgpgYGB7cn0Kc2F2ZShlZGdlX2NvbmRfdW1hcF9kZiwgcG9pbnRfY29uZF91bWFwX2RmLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvbmV0d29ya3NfY29uZF91bWFwLlJEYXRhIikKc2F2ZShwZV91bWFwX2wsIHBlX3VtYXBfY29uZF9sX2NoLCAKICAgICBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvbWVkaWFuX25ldHdvcmtzX2NvbmRfdW1hcC5SRGF0YSIpCmBgYAoKCgo=